# resource_properties_data

created_at: 2026-06-13 07:31:59.616197
data:
  {"tuple:('role_data', 'service_config_settings')": null, "tuple:('role_data', 'config_settings')": {"neutron::notification_driver": ["noop"], "neutron::core_plugin": "ml2", "neutron::service_plugins": ["qos", "ovn-router", "trunk", "segments", "port_forwarding", "log"], "neutron::report_interval": 300, "neutron::logging::debug": true, "neutron::purge_config": false, "neutron::allow_overlapping_ips": true, "neutron::dhcp_agent_notification": true, "neutron::dns_domain": "ooo.test", "neutron::rabbit_heartbeat_timeout_threshold": 60, "neutron::host": "%{hiera('fqdn_canonical')}", "neutron::db::database_db_max_retries": -1, "neutron::db::database_max_retries": -1, "neutron::db::sync::db_sync_timeout": 900, "neutron::global_physnet_mtu": 1500, "neutron::db::sync::extra_params": "", "neutron::vlan_transparent": false, "neutron::rabbit_heartbeat_in_pthread": false}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-33-ol5oesruk6ik-NeutronBase-4677wftz4qvj/1996669b-d14b-48b7-8b1b-e8b87ebd7b2e"}


created_at: 2026-06-13 07:31:59.622817
data:
  {"value": {"ContainerNeutronDHCPImage": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "ContainerNeutronConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-neutron-server:17.1"}}


created_at: 2026-06-13 07:31:59.709615
data:
  {"cmd_extra_args": "--log-file=/var/log/neutron/dhcp-agent.log", "volumes": ["/var/log/containers/neutron:/var/log/neutron:z"], "config_settings": null, "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "state": "directory", "setype": "{{ item.setype }}", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/neutron", "setype": "container_file_t", "mode": "0750"}]}], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-33-ol5oesruk6ik-NeutronLogging-wcp5luthiivu/8e26f02d-9ca7-43e3-970a-1c9d5c3c738a"}


created_at: 2026-06-13 07:31:59.729447
data:
  {"tuple:('role_data', 'step_config')": null, "tuple:('role_data', 'container_config_scripts')": {"container_puppet_apply.sh": {"mode": "0700", "content": "#!/bin/bash\nset -eux\nSTEP=$1\nTAGS=$2\nCONFIG=$3\nEXTRA_ARGS=${4:-''}\nif [ -d /tmp/puppet-etc ]; then\n  # ignore copy failures as these may be the same file depending on docker mounts\n  cp -a /tmp/puppet-etc/* /etc/puppet || true\nfi\necho \"{\\\"step\\\": ${STEP}}\" > /etc/puppet/hieradata/docker_puppet.json\n# $::deployment_type in puppet-tripleo\nexport FACTER_deployment_type=containers\nset +e\npuppet apply $EXTRA_ARGS \\\n    --verbose \\\n    --detailed-exitcodes \\\n    --summarize \\\n    --color=false \\\n    --modulepath /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules \\\n    --tags $TAGS \\\n    -e \"noop_resource('package'); ${CONFIG}\"\nrc=$?\nset -e\nset +ux\nif [ $rc -eq 2 -o $rc -eq 0 ]; then\n    exit 0\nfi\nexit $rc\n"}, "pyshim.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n#\n# Usage: pyshim.sh <script and/or arguments>\n#\n# Unfortunately THT doesn't know which version of python might be in a\n# container so we need this script to be able to try python3 or python2\n# depending on availability.  Since this is a temporary shim until we've\n# fully cut over to python3, we check for the existence of python3 first\n# before falling back to python2. This will help in the transition from\n# python2 based containers to python3.\n\nshow_usage() {\n    echo \"Usage: pyshim.sh <script and/or arguments>\"\n}\n\nif [ $# -lt 1 ]\nthen\n    show_usage\n    exit 1\nfi\n\nset -x\nif command -v python3 >/dev/null; then\n    python3 \"$@\"\nelif command -v python2 >/dev/null; then\n    python2 \"$@\"\nelif command -v python >/dev/null; then\n    python \"$@\"\nelse\n    echo \"ERROR: python is not available!\"\n    exit 1\nfi\n"}, "pacemaker_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nset -u\n\n# ./pacemaker_restart_bundle.sh mysql galera galera-bundle Master _\n# ./pacemaker_restart_bundle.sh redis redis redis-bundle Slave Master\n# ./pacemaker_restart_bundle.sh ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\nRESTART_SCRIPTS_DIR=$(dirname $0)\nTRIPLEO_SERVICE=$1\nRESOURCE_NAME=$2\nBUNDLE_NAME=$3\nWAIT_TARGET_LOCAL=$4\nWAIT_TARGET_ANYWHERE=${5:-_}\nTRIPLEO_MINOR_UPDATE=\"${TRIPLEO_MINOR_UPDATE:-false}\"\nTRIPLEO_HA_WRAPPER_RESOURCE_EXISTS=\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS:-false}\"\n\nbundle_can_be_restarted() {\n    local bundle=$1\n    # As long as the resource bundle is managed by pacemaker and is\n    # not meant to stay stopped, no matter the state of any inner\n    # pcmk_remote or ocf resource, we should restart it to give it a\n    # chance to read the new config.\n    [ \"$(crm_resource --meta -r $1 -g is-managed 2>/dev/null)\" != \"false\" ] && \\\n    [ \"$(crm_resource --meta -r $1 -g target-role 2>/dev/null)\" != \"Stopped\" ]\n}\n\nlog() {\n    local msg=$1\n    logger -t pcmkrestart \"$1\"\n}\n\nHOSTNAME=$(/bin/hostname -s)\nif [ x\"${TRIPLEO_MINOR_UPDATE,,}\" != x\"true\" ]; then\n    if [ x\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS,,}\" = x\"false\" ]; then\n        # Do not restart during initial deployment, as the resource\n        # has just been created.\n        SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n        if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n            log \"Initial deployment, skipping the restart of ${BUNDLE_NAME}\"\n\tfi\n        exit 0\n    else\n        # During a stack update, this script is called in parallel on\n        # every node the resource runs on, after the service's configs\n        # have been updated on all nodes. So we need to run pcs only\n        # once (e.g. on the service's boostrap node).\n        if bundle_can_be_restarted ${BUNDLE_NAME}; then\n            SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n            if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n                replicas_running=$(crm_resource -Q -r $BUNDLE_NAME --locate 2>&1 | wc -l)\n                if [ \"$replicas_running\" != \"0\" ]; then\n                    log \"Restarting ${BUNDLE_NAME} globally. Stopping:\"\n                    /sbin/pcs resource disable --wait=600 $BUNDLE_NAME\n                    log \"Restarting ${BUNDLE_NAME} globally. Starting:\"\n                    /sbin/pcs resource enable --wait=600 $BUNDLE_NAME\n                else\n                    log \"${BUNDLE_NAME} is not running anywhere,\" \\\n                         \"cleaning up to restart it globally if necessary\"\n                    /sbin/pcs resource cleanup $BUNDLE_NAME\n                fi\n            else\n                log \"Skipping global restart of ${BUNDLE_NAME} on ${HOSTNAME} it will be restarted by node ${SERVICE_NODEID}\"\n            fi\n\n        else\n            log \"No global restart needed for ${BUNDLE_NAME}.\"\n        fi\n    fi\nelse\n    # During a minor update workflow however, a host gets fully\n    # updated before updating the next one. So unlike stack\n    # update, at the time this script is called, the service's\n    # configs aren't updated on all nodes yet. So only restart the\n    # resource locally, where it's guaranteed that the config is\n    # up to date.\n    HOST=$(facter hostname)\n\n    if bundle_can_be_restarted ${BUNDLE_NAME}; then\n\t# if the resource is running locally, restart it\n\tif crm_resource -r $BUNDLE_NAME --locate 2>&1 | grep -w -q \"${HOST}\"; then\n            log \"Restarting ${BUNDLE_NAME} locally on '${HOST}'\"\n            /sbin/pcs resource restart $BUNDLE_NAME \"${HOST}\"\n\n\telse\n\t    # At this point, if no resource is running locally, it's\n\t    # either because a) it has failed previously, or b) because\n\t    # it's an A/P resource running elsewhere.\n\t    # By cleaning up resource, we ensure that a) it will try to\n\t    # restart, or b) it won't do anything if the resource is\n\t    # already running elsewhere.\n            log \"${BUNDLE_NAME} is currently not running on '${HOST}',\" \\\n                 \"cleaning up its state to restart it if necessary\"\n            /sbin/pcs resource cleanup $BUNDLE_NAME node=\"${HOST}\"\n\tfi\n\n\t# Wait until the resource is in the expected target state\n\t$RESTART_SCRIPTS_DIR/pacemaker_wait_bundle.sh \\\n            $RESOURCE_NAME $BUNDLE_NAME \\\n            \"$WAIT_TARGET_LOCAL\" \"$WAIT_TARGET_ANYWHERE\" \\\n\t    \"${HOST}\" 600\n    else\n        log \"No restart needed for ${BUNDLE_NAME}.\"\n    fi\nfi\n"}, "pacemaker_wait_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# ----\n# Wait for an OCF resource or a bundle to be restarted\n# ----\n# e.g.:\n# M/S OCF:      $0 galera galera-bundle Master\n# clone OCF:    $0 rabbitmq rabbitmq-bundle Started\n# A/P M/S OCF:  $0 redis redis-bundle Slave Master\n# A/P bundle:   $0 openstack-cinder-volume openstack-cinder-volume _ Started\n# clone bundle: $0 haproxy-bundle haproxy-bundle Started\n\n# design note 1:\n#  - this script is called during a minor update; it is called\n#    once per node that hosts a service replica.\n#  - the purpose of this script is to ensure that restarting the\n#    service replica locally won't disrupt the service availability\n#    for the end user. To reach that goal, the script waits until the\n#    service is restarted locally or globallu and reaches a given\n#    target state (i.e. Started, Slave or Master).\n# design note 2:\n#   - we don't want to track restart error: our only job is to ensure\n#     service restart synchronization, not service health.\n#   - In particular, we don't want to error out in case the resource\n#     cannot be restarted locally, because that would make the minor\n#     update fail, even if potentially other replicas still provide\n#     the service.\n# design note 3:\n#   - we can bail out early if we determine that the resource can't\n#     be restarted automatically by pacemaker (e.g. its \"blocked\",\n#     unmanaged or disabled).\n\nlog() {\n    local msg=$1\n    echo \"$(date -u): $1\"\n}\n\nusage() {\n    echo 2>&1 \"Usage: $0 NAME BUNDLE_NAME ROLE_LOCAL [ROLE_ANYWHERE] [HOST] [TIMEOUT]\"\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\n#\n# Utility functions to detect stuck resources\n#\n\nbundle_failures_locally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local replica_name\n    for i in $(seq 0 $last); do\n\treplica_name=${BUNDLE_NAME}-${engine}-${i}\n\tcrm_failcount -q -G -r $replica_name -N $HOST\n    done\n}\n\nbundle_failures_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    for i in $(seq 0 $last); do\n\tcrm_failcount -q -G -r ${BUNDLE_NAME}-${engine}-${i}\n    done\n}\n\nbundle_running_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    # return the number of running bundles replica, i.e. the number of\n    # docker/podman resource replicas currently running in the cluster\n    crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:heartbeat:${engine}']/node)\" -\n}\n\nocf_failures_globally() {\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local bundle_node\n    for i in $(seq 0 $last); do\n\tbundle_node=${BUNDLE_NAME}-${i}\n\tcrm_failcount -q -G -r $NAME -N $bundle_node\n    done\n}\n\ndid_resource_failed_locally() {\n    local failures\n    local running\n    local remotehost\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# if we're dealing with an ocf resource, it is running on a\n\t# pacemaker_remote rather that on the real host, and the\n\t# failcounts are thus associated to the pcmk remote. Replace\n\t# the host's name with the pcmk remote's name.\n        remotehost=$(crm_mon --as-xml | xmllint --xpath \"string(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource/node[@name='${HOST}']/../../resource[@resource_agent='${OCF}:pacemaker:remote']/@id)\" -)\n\tif [ -n \"${remotehost}\" ]; then\n\t    crm_failcount -q -G -r $NAME -N $remotehost | grep -q -w INFINITY\n\t    return $?\n\tfi\n\t# If no pcmk remote is currently running, the failcount from\n\t# the ocf resource is useless, compute the failcount from the\n\t# bundle case instead (computed below).\n    fi\n\n    # for bundles, pacemaker can run any bundle replica locally\n    # (e.g. galera-bundle-docker-{0,1,2}), and a failure happens when\n    # there are no more replica to try.\n    # That is, when _at least_ one replica failed locally, and all the\n    # others either failed or are currently running elsewhere.\n    failures=$(bundle_failures_locally $HOST | grep -c -w INFINITY)\n    running=$(bundle_running_globally)\n    test $failures -gt 0 && \\\n    test $(( $failures + $running )) -ge $BUNDLE_REPLICAS\n}\n\ndid_resource_failed_globally() {\n    local remotecount\n    local failures\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# we check the state of an ocf resource only if the\n\t# pcmkremotes are started\n        remotecount=$(crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node)\" -)\n\tif [ \"${remotecount}\" = \"0\" ]; then\n\t    # no pcmkremote is running, so check the bundle state\n\t    # instead of checking the ocf resource\n\t    # bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\t    failures=$(bundle_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\telse\n\t    # ocf resource failed if it failed to start on\n\t    # all $BUNDLE_REPLICAS bundle nodes\n\t    failures=$(ocf_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\tfi\n    else\n\t# bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\tfailures=$(bundle_failures_globally | grep -c -w INFINITY)\n\ttest $failures -eq $BUNDLE_REPLICAS\n    fi\n}\n\n\n# Input validation\n#\n\nNAME=$1\nif [ -z \"${NAME}\" ]; then\n    echo 2>&1 \"Error: argument NAME must not be empty\"\n    exit 1\nfi\n\nBUNDLE_NAME=$2\nif [ -z \"${BUNDLE_NAME}\" ]; then\n    echo 2>&1 \"Error: argument BUNDLE_NAME must not be empty\"\n    exit 1\nfi\n\nROLE_LOCAL=$3\nif [ \"${ROLE_LOCAL}\" = \"_\" ]; then\n    ROLE_LOCAL=\"\"\nfi\n\nROLE_ANYWHERE=$4\nif [ \"${ROLE_ANYWHERE}\" = \"_\" ]; then\n    ROLE_ANYWHERE=\"\"\nfi\n\nif [ -z \"${ROLE_LOCAL}\" ]; then\n    if [ -z \"${ROLE_ANYWHERE}\" ]; then\n        echo 2>&1 \"Error: either ROLE_LOCAL or ROLE_ANYWHERE must be non empty\"\n        exit 1\n    fi\nelse\n    if !(echo \"${ROLE_LOCAL}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n        echo 2>&1 \"Error: argument ROLE_LOCAL must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n        exit 1\n    fi\nfi\n\nif [ -n \"${ROLE_ANYWHERE}\" ] && !(echo \"${ROLE_ANYWHERE}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n    echo 2>&1 \"Error: argument ROLE_ANYWHERE must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n    exit 1\nfi\n\n# Ensure compatibility with pacemaker 2.1\nif pacemaker_supports_promoted; then\n    ROLE_LOCAL=$(echo \"$ROLE_LOCAL\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    ROLE_ANYWHERE=$(echo \"$ROLE_ANYWHERE\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    OCF=\"ocf\"\nelse\n    OCF=\"ocf:\"\nfi\n\nHOST=${5:-$(facter hostname)}\nTIMEOUT=${6:-600}\n\n\n# Configure the search\n# ----\n# Note: we can't use crm_resource in all searches because we can't\n# easily extract the host the OCF resources run on (crm_resource\n# returns the pcmk-remote nodes rather than the hosts)\n# So instead, we implement various searches with XPath directly.\n\nif [ \"${BUNDLE_NAME}\" != \"${NAME}\" ]; then\n# ocf resource\nlocal_resource_xpath=\"//bundle/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node[@name='${HOST}']/../../resource[@id='${NAME}']\"\nany_resource_xpath=\"//bundle//resource[@id='${NAME}']\"\nreplicas_xpath=\"//bundle/primitive[@id='${BUNDLE_NAME}']/../*[boolean(@image) and boolean(@replicas)]\"\nelse\n# bundle resource\nlocal_resource_xpath=\"//bundle[@id='${NAME}']/replica/resource/node[@name='${HOST}']/../../resource\"\nany_resource_xpath=\"//bundle[@id='${NAME}']//resource\"\nreplicas_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nfi\n\nbundle_def_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nBUNDLE_CONTAINER_ENGINE=$(cibadmin -Q | xmllint --xpath \"name(${bundle_def_xpath})\" -)\nBUNDLE_REPLICAS=$(cibadmin -Q | xmllint --xpath \"string(${bundle_def_xpath}/@replicas)\" -)\n\n\n# The wait algorithm follows a two-stage approach\n#  1. Depending on how the script is called, we first check whether\n#     the resource is restarted locally. An A/P resource may be\n#     restarted elsewhere in the cluster.\n#  2. If needed, check whether the A/P resource has restarted\n#     elsewhere. For A/P M/S resources, in case the resource is\n#     restarted as Slave locally, ensure a Master is available.\n\nsuccess=1\nbailout=1\ntimeout=$TIMEOUT\nrole=\"\"\n\n# Stage 1: local check\nif [ -n \"$ROLE_LOCAL\" ]; then\n    log \"Waiting until ${NAME} has restarted on ${HOST} and is in state ${ROLE_LOCAL}\"\n    log \"Will probe resource state with the following XPath pattern: ${local_resource_xpath}\"\n\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resource=$(crm_mon -r --as-xml | xmllint --xpath \"${local_resource_xpath}\" - 2>/dev/null)\n        role=$(echo \"${resource}\" | sed -ne 's/.*\\Wrole=\"\\([^\"]*\\)\".*/\\1/p')\n\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resource}\" | grep -q -w \"\\Wblocked=\\\"true\\\"\"; then\n            log \"${NAME} is blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_locally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif [ \"$role\" = \"$ROLE_LOCAL\" ]; then\n            success=0\n            continue\n        elif [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" = \"$ROLE_ANYWHERE\" ]; then\n            # A/P: we are restarted in the expected state\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_LOCAL} on ${HOST}\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\n# Stage 2: global check\nif [ $timeout -gt 0 ] && [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" != \"$ROLE_ANYWHERE\" ]; then\n    log \"Wait until ${NAME} is restarted anywhere in the cluster in state ${ROLE_ANYWHERE}\"\n    log \"Will probe resource state with the following XPath pattern: ${any_resource_xpath}\"\n\n    success=1\n    bailout=1\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resources=$(crm_mon -r --as-xml | xmllint --xpath \"${any_resource_xpath}\" - 2>/dev/null)\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif ! (echo \"${resources}\" | grep -q -w \"\\Wblocked=\\\"false\\\"\"); then\n            log \"${NAME} blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_globally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resources}\" | grep -q -w \"\\Wrole=\\\"${ROLE_ANYWHERE}\\\"\"; then\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_ANYWHERE} anywhere in the cluster\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\nif [ $timeout -le 0 ]; then\n    log \"Timeout reached after ${TIMEOUT}s while waiting for ${NAME} to be restarted\"\nelif [ $bailout -le 0 ]; then\n    log \"Restart monitoring for ${NAME} cancelled\"\nfi\n\nif [ $success -eq 0 ]; then\n    log \"${NAME} successfully restarted\"\nelse\n    log \"${NAME} was not restarted properly\"\nfi\n\n# Don't block minor update or stack update if the wait was unsuccessful\nexit 0\n"}, "wait-port-and-run.sh": {"mode": "0755", "content": "#!/usr/bin/bash\nset -eu\n\nHOST=$1\nPORT=$2\n\necho \"$(date -u): Checking whether we can bind to ${HOST}:${PORT}\"\nwhile (ss -Htnl src \"${HOST}\" \"sport = :${PORT}\" | grep -wq \"${PORT}\"); do\n    echo \"$(date -u): ${HOST}:${PORT} still in use, waiting...\";\n    sleep 10;\ndone\n\nshift 2\nCOMMAND=\"$*\"\nif [ -z \"${COMMAND}\" ]; then\n    COMMAND=\"true\"\nfi\nexec $COMMAND\n"}, "pacemaker_resource_lock.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nMAX_RETRIES=10\nCIB_ENOTFOUND=105\n\nusage() {\n   echo \"Set a global property in the cluster with a validity timestamp.\"\n   echo \"Usage:\"\n   echo \"   $0 --acquire <lock_name> <lock_owner> <lock_ttl_in_seconds>\"\n   echo \"   $0 --release <lock_name> <lock_owner>\"\n   echo\n}\n\nlog() {\n    echo \"$(date -u): $1\" 1>&2\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\nlock_create() {\n    local name=$1\n    local data=$2\n    # cibadmin won't overwrite a key if someone else succeeded to create it concurrently\n    cibadmin --sync-call --scope crm_config --create --xml-text \"<cluster_property_set id='${name}'><nvpair id='${name}-pair' name='${name}' value='${data}'/></cluster_property_set>\" &>/dev/null\n    return $?\n}\n\nlock_update() {\n    local name=$1\n    local expected_data=$2\n    local new_data=$3\n    # we only update the lock we expect to see, so we can't update someone else's lock\n    cibadmin --sync-call --scope crm_config --modify --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" --xml-text \"<nvpair id='${name}-pair' name='${name}' value='${new_data}'/>\" &>/dev/null\n    return $?\n}\n\nlock_delete() {\n    local name=$1\n    local expected_data=$2\n    # we only delete the lock we expect to see, so we can't delete someone else's lock\n    cibadmin --sync-call --scope crm_config --delete --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" &>/dev/null\n    return $?\n}\n\nlock_get() {\n    local lockname=$1\n    local res\n    local rc\n    res=$(cibadmin --query --scope crm_config --xpath \"//cluster_property_set/nvpair[@name='$lockname']\" 2>/dev/null)\n    rc=$?\n    if [ $rc -eq 0 ]; then\n        echo \"$res\" | sed -n 's/.*value=\"\\([^\"]*\\)\".*/\\1/p'\n    fi\n    return $rc\n}\n\nlock_owner() {\n    local lock=$1\n    echo \"$lock\" | cut -d':' -f1\n}\n\nlock_has_expired() {\n    local lock=$1\n    local expiry=$(echo \"$lock\" | cut -d':' -f2)\n    local now=$(date +%s)\n    test $now -ge $expiry\n}\n\n\n# Perform a lock action and restart if the CIB has been modified before\n# committing the lock action\ntry_action() {\n    local fun=$1\n    local lock=$2\n    local requester=$3\n    local args=${4:-}\n    local tries=$MAX_RETRIES\n    local rc=1\n    if [ \"$fun\" = \"lock_acquire\" ] || [ \"$fun\" = \"lock_release\" ]; then\n        log \"Try running $fun\"\n    else\n        return 2\n    fi\n    while [ $rc -ne 0 ]; do\n        $fun $lock $requester $args\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Operation $1 succeeded\"\n            return 0\n        elif [ $rc -eq 3 ]; then\n            # rc == 3 -> CIB changed before push\n            if [ $tries -eq 0 ]; then\n                log \"Failed to commit after $MAX_RETRIES retries. Bailing out.\"\n                return 2\n            else\n                log \"Failed to commit. Retrying operation.\"\n                tries=$(($tries - 1))\n            fi\n        elif [ $rc -eq 2 ]; then\n            # rc == 2 -> unrecoverable cib error (e.g. pacemaker down)\n            log \"Unexpected failure. Bailing out\"\n            return $rc\n        else\n            # rc == 1 -> lock error (not owner, lock doesn't exists)\n            return $rc\n        fi\n    done\n}\n\n# The lock mechanism uses cibadmin's atomic creation so cluster-wide\n# state coherency is guaranteed by pacemaker\nlock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc\n    local lock\n    local expiry\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -n \"$lock\" ]; then\n        lock_has_expired $lock\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Lock has expired, now available for being held\"\n        else\n            # lock is still held. check whether we're the owner\n            owner=$(lock_owner $lock)\n            if [ \"$owner\" = \"$requester\" ];then\n                log \"Requester already owns the lock, acquiring attempt will just reconfigure the TTL\"\n            else\n                log \"Lock is held by someone else ($owner)\"\n                return 1\n            fi\n        fi\n    else\n        log \"Lock is not held yet\"\n    fi\n\n    # prepare the lock info\n    expiry=$(($(date +%s) + $ttl))\n\n    if [ -n \"$lock\" ]; then\n        log \"Attempting to update the lock\"\n        lock_update $lockname \"$lock\" \"$requester:$expiry\"\n        rc=$?\n    else\n        log \"Attempting to acquire the lock\"\n        lock_create $lockname \"$requester:$expiry\"\n        rc=$?\n    fi\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' acquired by '$requester', valid until $(date -d @$expiry)\"\n        return 0\n    else\n        log \"CIB changed, lock cannot be acquired\"\n        return 3\n    fi\n}\n\n\n# The lock mechanism uses the CIB's num_updates tag to implement\n# a conditional store. Cluster-wide locking is guaranteed by pacemaker\nlock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n    local lock\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        log \"Lock doesn't exist. Nothing to release\"\n        return 0\n    else\n        log \"Lock exists, check whether we're the owner\"\n        owner=$(lock_owner $lock)\n        if [ \"$owner\" != \"$requester\" ];then\n            log \"Lock is held by someone else ($owner), will not unlock\"\n            return 1\n        fi\n    fi\n\n    lock_delete $lockname \"$lock\"\n    rc=$?\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' released by '$requester'\"\n        return 0\n    else\n        log \"CIB deletion error, lock cannot be released\"\n        return 3\n    fi\n}\n\n\n# Retrieve the owner of a lock from the CIB\n# this is a read-only operation, so no need to log debug info\nlock_get_owner() {\n    local lockname=$1\n    local rc\n    local lock\n    local owner\n\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        return 1\n    else\n        lock_owner $lock\n        return 0\n    fi\n}\n\n\nACTION=$1\nLOCKNAME=$2\nREQUESTER=$3\nTTL=${4:-60}\n\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nif [ $ACTION != \"--help\" ]; then\n    if [ -z \"$LOCKNAME\" ]; then\n        error \"You must specific a lock name\"\n    fi\n    if [ $ACTION != \"--owner\" ] && [ $ACTION != \"-o\" ]; then\n        if [ -z \"$REQUESTER\" ]; then\n            error \"You must specific a lock requester\"\n        fi\n    fi\nfi\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) try_action lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --release|-r) try_action lock_release $LOCKNAME $REQUESTER;;\n    --acquire-once|-A) lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --owner|-o) lock_get_owner $LOCKNAME;;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "pacemaker_mutex_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_restart_bundle.sh --lock mysql galera galera-bundle Master _\n# pacemaker_mutex_restart_bundle.sh --lock ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\n\nset -u\n\nusage() {\n    echo \"Restart a clustered resource in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --lock <tripleo-service> <pcmk-resource> <pcmk-bundle> <target-state-local> <target-state-cluster>\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\nACTION=$1\ncase $ACTION in\n    --help) usage; exit 0;;\n    --lock) ;;\n    *) error \"Unknown action '$ACTION'\";;\nesac\n\nTRIPLEO_SERVICE=$2\nLOCK_NAME=${TRIPLEO_SERVICE}-restart-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to restart resource $TRIPLEO_SERVICE\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\nRESOURCE_NAME=$3\nBUNDLE_NAME=$4\nWAIT_TARGET_LOCAL=$5\nWAIT_TARGET_ANYWHERE=${6:-_}\n\nif pacemaker_supports_promoted; then\n    WAIT_TARGET_LOCAL=$(echo \"$5\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    WAIT_TARGET_ANYWHERE=$(echo \"${6:-_}\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    promoted_role=\"Promoted\"\nelse\n    promoted_role=\"Master\"\nfi\n\n# The lock TTL should accomodate for the resource start/promote timeout\nif [ \"$RESOURCE_NAME\" != \"$BUNDLE_NAME\" ]; then\n    if [ \"$WAIT_TARGET_LOCAL\" = \"$promoted_role\" ] || [ \"$WAIT_TARGET_ANYWHERE\" = \"$promoted_role\" ]; then\n        rsc_op=\"promote\"\n    else\n        rsc_op=\"start\"\n    fi\n    # <op id=\"galera-promote-interval-0s\" interval=\"0s\" name=\"promote\" on-fail=\"block\" timeout=\"300s\"/>\n    PCMK_TTL=$(cibadmin -Q | xmllint -xpath \"string(//primitive[@id='${RESOURCE_NAME}']/operations/op[@name='${rsc_op}']/@timeout)\" - | sed 's/s$//')\n    LOCK_TTL=$((PCMK_TTL + 30))\nelse\n    # The podman RA's default start timeout\n    LOCK_TTL=90\nfi\n\nlog \"Acquire a ${LOCK_TTL}s restart lock for service $TRIPLEO_SERVICE before restarting it\"\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nrc=1\nwhile [ $rc -ne 0 ]; do\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire $LOCK_NAME $LOCK_OWNER $LOCK_TTL\n    rc=$?\n    if [ $rc != 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not acquire lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Could not acquire lock, retrying\"\n            sleep 10\n        fi\n    fi\ndone\n\nlog \"Restart the service $TRIPLEO_SERVICE locally\"\n# Reuse the local restart script in t-h-t (driven by env var TRIPLEO_MINOR_UPDATE)\nTRIPLEO_MINOR_UPDATE=true /var/lib/container-config-scripts/pacemaker_restart_bundle.sh $TRIPLEO_SERVICE $RESOURCE_NAME $BUNDLE_NAME $WAIT_TARGET_LOCAL $WAIT_TARGET_ANYWHERE\n\n# If we reached this point, always try to release the lock\nlog \"Release the restart lock for service $TRIPLEO_SERVICE\"\n/var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $LOCK_NAME $LOCK_OWNER\nrc=$?\nif [ $rc -ne 0 ] && [ $rc -ne 1 ]; then\n    error \"Could not release held lock (rc: $rc)\"\nfi\n"}, "pacemaker_mutex_shutdown.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_shutdown.sh --acquire\n# pacemaker_mutex_shutdown.sh --release\n\nset -u\n\nusage() {\n    echo \"Shutdown a cluster node in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --acquire # prevent other node from shutting down until we hold the lock\"\n    echo \"   $0 --release # release the lock, other node can compete for the shutdown lock\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nshutdown_lock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc=1\n    local current_owner\n    local owner_stopped\n    local owner_rc\n\n    log \"Acquiring the shutdown lock\"\n    while [ $rc -ne 0 ]; do\n        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire-once $lockname $requester $ttl\n        rc=$?\n        if [ $rc -ne 0 ]; then\n            if [ $rc -eq 2 ]; then\n                error \"Could not acquire the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n            else\n                # The lock is held by another node.\n                current_owner=$(/var/lib/container-config-scripts/pacemaker_resource_lock.sh --owner $lockname)\n                owner_rc=$?\n                if [ $owner_rc -eq 2 ]; then\n                    error \"Could not get the shutdown lock owner due to unrecoverable error (rc: $owner_rc), bailing out\"\n                fi\n                if [ $owner_rc -eq 0 ]; then\n                    # If the owner is marked as offline, that means it has shutdown and\n                    # we can clean the lock preemptively and try to acquire it.\n                    owner_stopped=$(crm_mon -1X | xmllint --xpath 'count(//nodes/node[@name=\"'${current_owner}'\" and @online=\"false\" and @unclean=\"false\"])' -)\n                    if [ \"${owner_stopped}\" = \"1\" ]; then\n                        log \"Shutdown lock held by stopped node '${current_owner}', lock can be released\"\n                        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $current_owner\n                        continue\n                    fi\n                fi\n                log \"Shutdown lock held by another node (rc: $rc), retrying\"\n                sleep 10\n            fi\n        fi\n    done\n    log \"Shutdown lock acquired\"\n    return 0\n}\n\n\n# Release the lock if we still own it. Not owning it anymore is not fatal\nshutdown_lock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n\n    log \"Releasing the shutdown lock\"\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $requester\n    rc=$?\n    if [ $rc -ne 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not release the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Shutdown lock no longer held, nothing to do\"\n        fi\n    else\n        log \"Shutdown lock released\"\n    fi\n    return 0\n}\n\n\nACTION=$1\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nLOCK_NAME=tripleo-shutdown-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to aquire the shutdown lock\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\n# We start with a very high TTL, that long enough to accomodate a cluster stop.\n# As soon as the node will get offline, the other competing node will be entitled\n# to steal the lock, so they should never wait that long in practice.\nLOCK_TTL=600\n\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) shutdown_lock_acquire ${LOCK_NAME} ${LOCK_OWNER} ${LOCK_TTL};;\n    --release|-r) shutdown_lock_release ${LOCK_NAME} ${LOCK_OWNER};;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}}, "tuple:('role_data', 'docker_config')": {"step_2": {"create_dnsmasq_wrapper": {"start_order": 1, "detach": false, "net": "host", "pid": "host", "user": "root", "command": ["/container_puppet_apply.sh", "4", "file", "include ::tripleo::profile::base::neutron::dhcp_agent_wrappers"], "image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/var/lib/container-config-scripts/container_puppet_apply.sh:/container_puppet_apply.sh:ro", "/etc/puppet:/tmp/puppet-etc:ro", "/usr/share/openstack-puppet/modules:/usr/share/openstack-puppet/modules:ro", "/run/openvswitch:/run/openvswitch:shared,z", "/var/lib/neutron:/var/lib/neutron:shared,z"]}}, "step_4": {"neutron_dhcp": {"start_order": 10, "image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "net": "host", "pid": "host", "cgroupns": "host", "privileged": true, "restart": "always", "security_opt": ["label=disable"], "depends_on": ["openvswitch.service"], "healthcheck": {"test": "/openstack/healthcheck 5672"}, "ulimit": ["nofile=16384"], "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/neutron:/var/log/neutron:z", "/var/lib/kolla/config_files/neutron_dhcp.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/neutron:/var/lib/kolla/config_files/src:ro", "/lib/modules:/lib/modules:ro", "/run/openvswitch:/run/openvswitch:shared,z", "/var/lib/neutron:/var/lib/neutron:shared,z", "/run/netns:/run/netns:shared", "/var/lib/neutron/kill_scripts:/etc/neutron/kill_scripts:shared,z", "/var/lib/neutron/dnsmasq_wrapper:/usr/local/bin/dnsmasq:ro", "/var/lib/neutron/dhcp_haproxy_wrapper:/usr/local/bin/haproxy:ro", "/etc/pki/tls/certs/neutron.crt:/var/lib/kolla/config_files/src-tls/etc/pki/tls/certs/neutron.crt:ro", "/etc/pki/tls/private/neutron.key:/var/lib/kolla/config_files/src-tls/etc/pki/tls/private/neutron.key:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "tuple:('role_data', 'service_name')": "neutron_dhcp", "role_data": {"service_name": "neutron_dhcp", "firewall_rules": {"115 neutron dhcp input": {"ipversion": "ipv4", "proto": "udp", "dport": 67}, "116 neutron dhcp output": {"ipversion": "ipv4", "proto": "udp", "chain": "OUTPUT", "dport": 68}, "115 neutron dhcpv6 input": {"ipversion": "ipv6", "proto": "udp", "dport": 547}, "116 neutron dhcpv6 output": {"ipversion": "ipv6", "proto": "udp", "chain": "OUTPUT", "dport": 546}, "116 neutron dhcpv6 relay output": {"ipversion": "ipv6", "proto": "udp", "chain": "OUTPUT", "dport": 547}}, "monitoring_subscription": "overcloud-neutron-dhcp", "config_settings": {"neutron::notification_driver": ["noop"], "neutron::core_plugin": "ml2", "neutron::service_plugins": ["qos", "ovn-router", "trunk", "segments", "port_forwarding", "log"], "neutron::report_interval": 300, "neutron::logging::debug": true, "neutron::purge_config": false, "neutron::allow_overlapping_ips": true, "neutron::dhcp_agent_notification": true, "neutron::dns_domain": "ooo.test", "neutron::rabbit_heartbeat_timeout_threshold": 60, "neutron::host": "%{hiera('fqdn_canonical')}", "neutron::db::database_db_max_retries": -1, "neutron::db::database_max_retries": -1, "neutron::db::sync::db_sync_timeout": 900, "neutron::global_physnet_mtu": 1500, "neutron::db::sync::extra_params": "", "neutron::vlan_transparent": false, "neutron::rabbit_heartbeat_in_pthread": false, "tripleo::profile::base::neutron::dhcp_agent_wrappers::enable_dnsmasq_wrapper": true, "tripleo::profile::base::neutron::dhcp_agent_wrappers::dnsmasq_process_wrapper": "/var/lib/neutron/dnsmasq_wrapper", "tripleo::profile::base::neutron::dhcp_agent_wrappers::dnsmasq_image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "tripleo::profile::base::neutron::dhcp_agent_wrappers::enable_haproxy_wrapper": true, "tripleo::profile::base::neutron::dhcp_agent_wrappers::haproxy_process_wrapper": "/var/lib/neutron/dhcp_haproxy_wrapper", "tripleo::profile::base::neutron::dhcp_agent_wrappers::haproxy_image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "tripleo::profile::base::neutron::dhcp_agent_wrappers::debug": true, "tripleo::profile::base::neutron::container_cli": "podman", "neutron::agents::dhcp::enable_isolated_metadata": false, "neutron::agents::dhcp::enable_force_metadata": true, "neutron::agents::dhcp::enable_metadata_network": false, "neutron::agents::dhcp::dnsmasq_local_resolv": false, "neutron::agents::dhcp::dnsmasq_dns_servers": [], "neutron::agents::dhcp::interface_driver": "neutron.agent.linux.interface.OVSInterfaceDriver", "neutron::agents::dhcp::dhcp_broadcast_reply": false, "neutron::agents::dhcp::dnsmasq_enable_addr6_list": true, "neutron::agents::dhcp::debug": true, "neutron::agents::dhcp::ovsdb_agent_ssl_key_file": "/etc/pki/tls/private/neutron.key", "neutron::agents::dhcp::ovsdb_agent_ssl_cert_file": "/etc/pki/tls/certs/neutron.crt", "neutron::agents::dhcp::ovsdb_agent_ssl_ca_file": "/etc/ipa/ca.crt"}, "service_config_settings": {"rsyslog": {"tripleo_logging_sources_neutron_dhcp": [{"tag": "openstack.neutron.agent.dhcp", "file": "/var/log/containers/neutron/dhcp-agent.log"}]}}, "puppet_config": {"config_volume": "neutron", "puppet_tags": "neutron_config,neutron_dhcp_agent_config", "step_config": "include tripleo::profile::base::neutron::dhcp\n", "config_image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-server:17.1"}, "kolla_config": {"/var/lib/kolla/config_files/neutron_dhcp.json": {"command": "/usr/bin/neutron-dhcp-agent --config-file /usr/share/neutron/neutron-dist.conf --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/dhcp_agent.ini --config-dir /etc/neutron/conf.d/common --config-dir /etc/neutron/conf.d/neutron-dhcp-agent --log-file=/var/log/neutron/dhcp-agent.log", "config_files": [{"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src-tls/*", "dest": "/", "merge": true, "preserve_properties": true, "optional": true}], "permissions": [{"path": "/var/log/neutron", "owner": "neutron:neutron", "recurse": true}, {"path": "/var/lib/neutron", "owner": "neutron:neutron", "recurse": true}, {"path": "/etc/pki/tls/certs/neutron.crt", "owner": "neutron:neutron"}, {"path": "/etc/pki/tls/private/neutron.key", "owner": "neutron:neutron"}]}}, "container_config_scripts": {"container_puppet_apply.sh": {"mode": "0700", "content": "#!/bin/bash\nset -eux\nSTEP=$1\nTAGS=$2\nCONFIG=$3\nEXTRA_ARGS=${4:-''}\nif [ -d /tmp/puppet-etc ]; then\n  # ignore copy failures as these may be the same file depending on docker mounts\n  cp -a /tmp/puppet-etc/* /etc/puppet || true\nfi\necho \"{\\\"step\\\": ${STEP}}\" > /etc/puppet/hieradata/docker_puppet.json\n# $::deployment_type in puppet-tripleo\nexport FACTER_deployment_type=containers\nset +e\npuppet apply $EXTRA_ARGS \\\n    --verbose \\\n    --detailed-exitcodes \\\n    --summarize \\\n    --color=false \\\n    --modulepath /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules \\\n    --tags $TAGS \\\n    -e \"noop_resource('package'); ${CONFIG}\"\nrc=$?\nset -e\nset +ux\nif [ $rc -eq 2 -o $rc -eq 0 ]; then\n    exit 0\nfi\nexit $rc\n"}, "pyshim.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n#\n# Usage: pyshim.sh <script and/or arguments>\n#\n# Unfortunately THT doesn't know which version of python might be in a\n# container so we need this script to be able to try python3 or python2\n# depending on availability.  Since this is a temporary shim until we've\n# fully cut over to python3, we check for the existence of python3 first\n# before falling back to python2. This will help in the transition from\n# python2 based containers to python3.\n\nshow_usage() {\n    echo \"Usage: pyshim.sh <script and/or arguments>\"\n}\n\nif [ $# -lt 1 ]\nthen\n    show_usage\n    exit 1\nfi\n\nset -x\nif command -v python3 >/dev/null; then\n    python3 \"$@\"\nelif command -v python2 >/dev/null; then\n    python2 \"$@\"\nelif command -v python >/dev/null; then\n    python \"$@\"\nelse\n    echo \"ERROR: python is not available!\"\n    exit 1\nfi\n"}, "pacemaker_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nset -u\n\n# ./pacemaker_restart_bundle.sh mysql galera galera-bundle Master _\n# ./pacemaker_restart_bundle.sh redis redis redis-bundle Slave Master\n# ./pacemaker_restart_bundle.sh ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\nRESTART_SCRIPTS_DIR=$(dirname $0)\nTRIPLEO_SERVICE=$1\nRESOURCE_NAME=$2\nBUNDLE_NAME=$3\nWAIT_TARGET_LOCAL=$4\nWAIT_TARGET_ANYWHERE=${5:-_}\nTRIPLEO_MINOR_UPDATE=\"${TRIPLEO_MINOR_UPDATE:-false}\"\nTRIPLEO_HA_WRAPPER_RESOURCE_EXISTS=\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS:-false}\"\n\nbundle_can_be_restarted() {\n    local bundle=$1\n    # As long as the resource bundle is managed by pacemaker and is\n    # not meant to stay stopped, no matter the state of any inner\n    # pcmk_remote or ocf resource, we should restart it to give it a\n    # chance to read the new config.\n    [ \"$(crm_resource --meta -r $1 -g is-managed 2>/dev/null)\" != \"false\" ] && \\\n    [ \"$(crm_resource --meta -r $1 -g target-role 2>/dev/null)\" != \"Stopped\" ]\n}\n\nlog() {\n    local msg=$1\n    logger -t pcmkrestart \"$1\"\n}\n\nHOSTNAME=$(/bin/hostname -s)\nif [ x\"${TRIPLEO_MINOR_UPDATE,,}\" != x\"true\" ]; then\n    if [ x\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS,,}\" = x\"false\" ]; then\n        # Do not restart during initial deployment, as the resource\n        # has just been created.\n        SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n        if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n            log \"Initial deployment, skipping the restart of ${BUNDLE_NAME}\"\n\tfi\n        exit 0\n    else\n        # During a stack update, this script is called in parallel on\n        # every node the resource runs on, after the service's configs\n        # have been updated on all nodes. So we need to run pcs only\n        # once (e.g. on the service's boostrap node).\n        if bundle_can_be_restarted ${BUNDLE_NAME}; then\n            SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n            if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n                replicas_running=$(crm_resource -Q -r $BUNDLE_NAME --locate 2>&1 | wc -l)\n                if [ \"$replicas_running\" != \"0\" ]; then\n                    log \"Restarting ${BUNDLE_NAME} globally. Stopping:\"\n                    /sbin/pcs resource disable --wait=600 $BUNDLE_NAME\n                    log \"Restarting ${BUNDLE_NAME} globally. Starting:\"\n                    /sbin/pcs resource enable --wait=600 $BUNDLE_NAME\n                else\n                    log \"${BUNDLE_NAME} is not running anywhere,\" \\\n                         \"cleaning up to restart it globally if necessary\"\n                    /sbin/pcs resource cleanup $BUNDLE_NAME\n                fi\n            else\n                log \"Skipping global restart of ${BUNDLE_NAME} on ${HOSTNAME} it will be restarted by node ${SERVICE_NODEID}\"\n            fi\n\n        else\n            log \"No global restart needed for ${BUNDLE_NAME}.\"\n        fi\n    fi\nelse\n    # During a minor update workflow however, a host gets fully\n    # updated before updating the next one. So unlike stack\n    # update, at the time this script is called, the service's\n    # configs aren't updated on all nodes yet. So only restart the\n    # resource locally, where it's guaranteed that the config is\n    # up to date.\n    HOST=$(facter hostname)\n\n    if bundle_can_be_restarted ${BUNDLE_NAME}; then\n\t# if the resource is running locally, restart it\n\tif crm_resource -r $BUNDLE_NAME --locate 2>&1 | grep -w -q \"${HOST}\"; then\n            log \"Restarting ${BUNDLE_NAME} locally on '${HOST}'\"\n            /sbin/pcs resource restart $BUNDLE_NAME \"${HOST}\"\n\n\telse\n\t    # At this point, if no resource is running locally, it's\n\t    # either because a) it has failed previously, or b) because\n\t    # it's an A/P resource running elsewhere.\n\t    # By cleaning up resource, we ensure that a) it will try to\n\t    # restart, or b) it won't do anything if the resource is\n\t    # already running elsewhere.\n            log \"${BUNDLE_NAME} is currently not running on '${HOST}',\" \\\n                 \"cleaning up its state to restart it if necessary\"\n            /sbin/pcs resource cleanup $BUNDLE_NAME node=\"${HOST}\"\n\tfi\n\n\t# Wait until the resource is in the expected target state\n\t$RESTART_SCRIPTS_DIR/pacemaker_wait_bundle.sh \\\n            $RESOURCE_NAME $BUNDLE_NAME \\\n            \"$WAIT_TARGET_LOCAL\" \"$WAIT_TARGET_ANYWHERE\" \\\n\t    \"${HOST}\" 600\n    else\n        log \"No restart needed for ${BUNDLE_NAME}.\"\n    fi\nfi\n"}, "pacemaker_wait_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# ----\n# Wait for an OCF resource or a bundle to be restarted\n# ----\n# e.g.:\n# M/S OCF:      $0 galera galera-bundle Master\n# clone OCF:    $0 rabbitmq rabbitmq-bundle Started\n# A/P M/S OCF:  $0 redis redis-bundle Slave Master\n# A/P bundle:   $0 openstack-cinder-volume openstack-cinder-volume _ Started\n# clone bundle: $0 haproxy-bundle haproxy-bundle Started\n\n# design note 1:\n#  - this script is called during a minor update; it is called\n#    once per node that hosts a service replica.\n#  - the purpose of this script is to ensure that restarting the\n#    service replica locally won't disrupt the service availability\n#    for the end user. To reach that goal, the script waits until the\n#    service is restarted locally or globallu and reaches a given\n#    target state (i.e. Started, Slave or Master).\n# design note 2:\n#   - we don't want to track restart error: our only job is to ensure\n#     service restart synchronization, not service health.\n#   - In particular, we don't want to error out in case the resource\n#     cannot be restarted locally, because that would make the minor\n#     update fail, even if potentially other replicas still provide\n#     the service.\n# design note 3:\n#   - we can bail out early if we determine that the resource can't\n#     be restarted automatically by pacemaker (e.g. its \"blocked\",\n#     unmanaged or disabled).\n\nlog() {\n    local msg=$1\n    echo \"$(date -u): $1\"\n}\n\nusage() {\n    echo 2>&1 \"Usage: $0 NAME BUNDLE_NAME ROLE_LOCAL [ROLE_ANYWHERE] [HOST] [TIMEOUT]\"\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\n#\n# Utility functions to detect stuck resources\n#\n\nbundle_failures_locally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local replica_name\n    for i in $(seq 0 $last); do\n\treplica_name=${BUNDLE_NAME}-${engine}-${i}\n\tcrm_failcount -q -G -r $replica_name -N $HOST\n    done\n}\n\nbundle_failures_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    for i in $(seq 0 $last); do\n\tcrm_failcount -q -G -r ${BUNDLE_NAME}-${engine}-${i}\n    done\n}\n\nbundle_running_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    # return the number of running bundles replica, i.e. the number of\n    # docker/podman resource replicas currently running in the cluster\n    crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:heartbeat:${engine}']/node)\" -\n}\n\nocf_failures_globally() {\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local bundle_node\n    for i in $(seq 0 $last); do\n\tbundle_node=${BUNDLE_NAME}-${i}\n\tcrm_failcount -q -G -r $NAME -N $bundle_node\n    done\n}\n\ndid_resource_failed_locally() {\n    local failures\n    local running\n    local remotehost\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# if we're dealing with an ocf resource, it is running on a\n\t# pacemaker_remote rather that on the real host, and the\n\t# failcounts are thus associated to the pcmk remote. Replace\n\t# the host's name with the pcmk remote's name.\n        remotehost=$(crm_mon --as-xml | xmllint --xpath \"string(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource/node[@name='${HOST}']/../../resource[@resource_agent='${OCF}:pacemaker:remote']/@id)\" -)\n\tif [ -n \"${remotehost}\" ]; then\n\t    crm_failcount -q -G -r $NAME -N $remotehost | grep -q -w INFINITY\n\t    return $?\n\tfi\n\t# If no pcmk remote is currently running, the failcount from\n\t# the ocf resource is useless, compute the failcount from the\n\t# bundle case instead (computed below).\n    fi\n\n    # for bundles, pacemaker can run any bundle replica locally\n    # (e.g. galera-bundle-docker-{0,1,2}), and a failure happens when\n    # there are no more replica to try.\n    # That is, when _at least_ one replica failed locally, and all the\n    # others either failed or are currently running elsewhere.\n    failures=$(bundle_failures_locally $HOST | grep -c -w INFINITY)\n    running=$(bundle_running_globally)\n    test $failures -gt 0 && \\\n    test $(( $failures + $running )) -ge $BUNDLE_REPLICAS\n}\n\ndid_resource_failed_globally() {\n    local remotecount\n    local failures\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# we check the state of an ocf resource only if the\n\t# pcmkremotes are started\n        remotecount=$(crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node)\" -)\n\tif [ \"${remotecount}\" = \"0\" ]; then\n\t    # no pcmkremote is running, so check the bundle state\n\t    # instead of checking the ocf resource\n\t    # bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\t    failures=$(bundle_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\telse\n\t    # ocf resource failed if it failed to start on\n\t    # all $BUNDLE_REPLICAS bundle nodes\n\t    failures=$(ocf_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\tfi\n    else\n\t# bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\tfailures=$(bundle_failures_globally | grep -c -w INFINITY)\n\ttest $failures -eq $BUNDLE_REPLICAS\n    fi\n}\n\n\n# Input validation\n#\n\nNAME=$1\nif [ -z \"${NAME}\" ]; then\n    echo 2>&1 \"Error: argument NAME must not be empty\"\n    exit 1\nfi\n\nBUNDLE_NAME=$2\nif [ -z \"${BUNDLE_NAME}\" ]; then\n    echo 2>&1 \"Error: argument BUNDLE_NAME must not be empty\"\n    exit 1\nfi\n\nROLE_LOCAL=$3\nif [ \"${ROLE_LOCAL}\" = \"_\" ]; then\n    ROLE_LOCAL=\"\"\nfi\n\nROLE_ANYWHERE=$4\nif [ \"${ROLE_ANYWHERE}\" = \"_\" ]; then\n    ROLE_ANYWHERE=\"\"\nfi\n\nif [ -z \"${ROLE_LOCAL}\" ]; then\n    if [ -z \"${ROLE_ANYWHERE}\" ]; then\n        echo 2>&1 \"Error: either ROLE_LOCAL or ROLE_ANYWHERE must be non empty\"\n        exit 1\n    fi\nelse\n    if !(echo \"${ROLE_LOCAL}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n        echo 2>&1 \"Error: argument ROLE_LOCAL must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n        exit 1\n    fi\nfi\n\nif [ -n \"${ROLE_ANYWHERE}\" ] && !(echo \"${ROLE_ANYWHERE}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n    echo 2>&1 \"Error: argument ROLE_ANYWHERE must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n    exit 1\nfi\n\n# Ensure compatibility with pacemaker 2.1\nif pacemaker_supports_promoted; then\n    ROLE_LOCAL=$(echo \"$ROLE_LOCAL\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    ROLE_ANYWHERE=$(echo \"$ROLE_ANYWHERE\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    OCF=\"ocf\"\nelse\n    OCF=\"ocf:\"\nfi\n\nHOST=${5:-$(facter hostname)}\nTIMEOUT=${6:-600}\n\n\n# Configure the search\n# ----\n# Note: we can't use crm_resource in all searches because we can't\n# easily extract the host the OCF resources run on (crm_resource\n# returns the pcmk-remote nodes rather than the hosts)\n# So instead, we implement various searches with XPath directly.\n\nif [ \"${BUNDLE_NAME}\" != \"${NAME}\" ]; then\n# ocf resource\nlocal_resource_xpath=\"//bundle/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node[@name='${HOST}']/../../resource[@id='${NAME}']\"\nany_resource_xpath=\"//bundle//resource[@id='${NAME}']\"\nreplicas_xpath=\"//bundle/primitive[@id='${BUNDLE_NAME}']/../*[boolean(@image) and boolean(@replicas)]\"\nelse\n# bundle resource\nlocal_resource_xpath=\"//bundle[@id='${NAME}']/replica/resource/node[@name='${HOST}']/../../resource\"\nany_resource_xpath=\"//bundle[@id='${NAME}']//resource\"\nreplicas_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nfi\n\nbundle_def_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nBUNDLE_CONTAINER_ENGINE=$(cibadmin -Q | xmllint --xpath \"name(${bundle_def_xpath})\" -)\nBUNDLE_REPLICAS=$(cibadmin -Q | xmllint --xpath \"string(${bundle_def_xpath}/@replicas)\" -)\n\n\n# The wait algorithm follows a two-stage approach\n#  1. Depending on how the script is called, we first check whether\n#     the resource is restarted locally. An A/P resource may be\n#     restarted elsewhere in the cluster.\n#  2. If needed, check whether the A/P resource has restarted\n#     elsewhere. For A/P M/S resources, in case the resource is\n#     restarted as Slave locally, ensure a Master is available.\n\nsuccess=1\nbailout=1\ntimeout=$TIMEOUT\nrole=\"\"\n\n# Stage 1: local check\nif [ -n \"$ROLE_LOCAL\" ]; then\n    log \"Waiting until ${NAME} has restarted on ${HOST} and is in state ${ROLE_LOCAL}\"\n    log \"Will probe resource state with the following XPath pattern: ${local_resource_xpath}\"\n\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resource=$(crm_mon -r --as-xml | xmllint --xpath \"${local_resource_xpath}\" - 2>/dev/null)\n        role=$(echo \"${resource}\" | sed -ne 's/.*\\Wrole=\"\\([^\"]*\\)\".*/\\1/p')\n\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resource}\" | grep -q -w \"\\Wblocked=\\\"true\\\"\"; then\n            log \"${NAME} is blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_locally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif [ \"$role\" = \"$ROLE_LOCAL\" ]; then\n            success=0\n            continue\n        elif [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" = \"$ROLE_ANYWHERE\" ]; then\n            # A/P: we are restarted in the expected state\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_LOCAL} on ${HOST}\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\n# Stage 2: global check\nif [ $timeout -gt 0 ] && [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" != \"$ROLE_ANYWHERE\" ]; then\n    log \"Wait until ${NAME} is restarted anywhere in the cluster in state ${ROLE_ANYWHERE}\"\n    log \"Will probe resource state with the following XPath pattern: ${any_resource_xpath}\"\n\n    success=1\n    bailout=1\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resources=$(crm_mon -r --as-xml | xmllint --xpath \"${any_resource_xpath}\" - 2>/dev/null)\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif ! (echo \"${resources}\" | grep -q -w \"\\Wblocked=\\\"false\\\"\"); then\n            log \"${NAME} blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_globally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resources}\" | grep -q -w \"\\Wrole=\\\"${ROLE_ANYWHERE}\\\"\"; then\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_ANYWHERE} anywhere in the cluster\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\nif [ $timeout -le 0 ]; then\n    log \"Timeout reached after ${TIMEOUT}s while waiting for ${NAME} to be restarted\"\nelif [ $bailout -le 0 ]; then\n    log \"Restart monitoring for ${NAME} cancelled\"\nfi\n\nif [ $success -eq 0 ]; then\n    log \"${NAME} successfully restarted\"\nelse\n    log \"${NAME} was not restarted properly\"\nfi\n\n# Don't block minor update or stack update if the wait was unsuccessful\nexit 0\n"}, "wait-port-and-run.sh": {"mode": "0755", "content": "#!/usr/bin/bash\nset -eu\n\nHOST=$1\nPORT=$2\n\necho \"$(date -u): Checking whether we can bind to ${HOST}:${PORT}\"\nwhile (ss -Htnl src \"${HOST}\" \"sport = :${PORT}\" | grep -wq \"${PORT}\"); do\n    echo \"$(date -u): ${HOST}:${PORT} still in use, waiting...\";\n    sleep 10;\ndone\n\nshift 2\nCOMMAND=\"$*\"\nif [ -z \"${COMMAND}\" ]; then\n    COMMAND=\"true\"\nfi\nexec $COMMAND\n"}, "pacemaker_resource_lock.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nMAX_RETRIES=10\nCIB_ENOTFOUND=105\n\nusage() {\n   echo \"Set a global property in the cluster with a validity timestamp.\"\n   echo \"Usage:\"\n   echo \"   $0 --acquire <lock_name> <lock_owner> <lock_ttl_in_seconds>\"\n   echo \"   $0 --release <lock_name> <lock_owner>\"\n   echo\n}\n\nlog() {\n    echo \"$(date -u): $1\" 1>&2\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\nlock_create() {\n    local name=$1\n    local data=$2\n    # cibadmin won't overwrite a key if someone else succeeded to create it concurrently\n    cibadmin --sync-call --scope crm_config --create --xml-text \"<cluster_property_set id='${name}'><nvpair id='${name}-pair' name='${name}' value='${data}'/></cluster_property_set>\" &>/dev/null\n    return $?\n}\n\nlock_update() {\n    local name=$1\n    local expected_data=$2\n    local new_data=$3\n    # we only update the lock we expect to see, so we can't update someone else's lock\n    cibadmin --sync-call --scope crm_config --modify --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" --xml-text \"<nvpair id='${name}-pair' name='${name}' value='${new_data}'/>\" &>/dev/null\n    return $?\n}\n\nlock_delete() {\n    local name=$1\n    local expected_data=$2\n    # we only delete the lock we expect to see, so we can't delete someone else's lock\n    cibadmin --sync-call --scope crm_config --delete --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" &>/dev/null\n    return $?\n}\n\nlock_get() {\n    local lockname=$1\n    local res\n    local rc\n    res=$(cibadmin --query --scope crm_config --xpath \"//cluster_property_set/nvpair[@name='$lockname']\" 2>/dev/null)\n    rc=$?\n    if [ $rc -eq 0 ]; then\n        echo \"$res\" | sed -n 's/.*value=\"\\([^\"]*\\)\".*/\\1/p'\n    fi\n    return $rc\n}\n\nlock_owner() {\n    local lock=$1\n    echo \"$lock\" | cut -d':' -f1\n}\n\nlock_has_expired() {\n    local lock=$1\n    local expiry=$(echo \"$lock\" | cut -d':' -f2)\n    local now=$(date +%s)\n    test $now -ge $expiry\n}\n\n\n# Perform a lock action and restart if the CIB has been modified before\n# committing the lock action\ntry_action() {\n    local fun=$1\n    local lock=$2\n    local requester=$3\n    local args=${4:-}\n    local tries=$MAX_RETRIES\n    local rc=1\n    if [ \"$fun\" = \"lock_acquire\" ] || [ \"$fun\" = \"lock_release\" ]; then\n        log \"Try running $fun\"\n    else\n        return 2\n    fi\n    while [ $rc -ne 0 ]; do\n        $fun $lock $requester $args\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Operation $1 succeeded\"\n            return 0\n        elif [ $rc -eq 3 ]; then\n            # rc == 3 -> CIB changed before push\n            if [ $tries -eq 0 ]; then\n                log \"Failed to commit after $MAX_RETRIES retries. Bailing out.\"\n                return 2\n            else\n                log \"Failed to commit. Retrying operation.\"\n                tries=$(($tries - 1))\n            fi\n        elif [ $rc -eq 2 ]; then\n            # rc == 2 -> unrecoverable cib error (e.g. pacemaker down)\n            log \"Unexpected failure. Bailing out\"\n            return $rc\n        else\n            # rc == 1 -> lock error (not owner, lock doesn't exists)\n            return $rc\n        fi\n    done\n}\n\n# The lock mechanism uses cibadmin's atomic creation so cluster-wide\n# state coherency is guaranteed by pacemaker\nlock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc\n    local lock\n    local expiry\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -n \"$lock\" ]; then\n        lock_has_expired $lock\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Lock has expired, now available for being held\"\n        else\n            # lock is still held. check whether we're the owner\n            owner=$(lock_owner $lock)\n            if [ \"$owner\" = \"$requester\" ];then\n                log \"Requester already owns the lock, acquiring attempt will just reconfigure the TTL\"\n            else\n                log \"Lock is held by someone else ($owner)\"\n                return 1\n            fi\n        fi\n    else\n        log \"Lock is not held yet\"\n    fi\n\n    # prepare the lock info\n    expiry=$(($(date +%s) + $ttl))\n\n    if [ -n \"$lock\" ]; then\n        log \"Attempting to update the lock\"\n        lock_update $lockname \"$lock\" \"$requester:$expiry\"\n        rc=$?\n    else\n        log \"Attempting to acquire the lock\"\n        lock_create $lockname \"$requester:$expiry\"\n        rc=$?\n    fi\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' acquired by '$requester', valid until $(date -d @$expiry)\"\n        return 0\n    else\n        log \"CIB changed, lock cannot be acquired\"\n        return 3\n    fi\n}\n\n\n# The lock mechanism uses the CIB's num_updates tag to implement\n# a conditional store. Cluster-wide locking is guaranteed by pacemaker\nlock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n    local lock\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        log \"Lock doesn't exist. Nothing to release\"\n        return 0\n    else\n        log \"Lock exists, check whether we're the owner\"\n        owner=$(lock_owner $lock)\n        if [ \"$owner\" != \"$requester\" ];then\n            log \"Lock is held by someone else ($owner), will not unlock\"\n            return 1\n        fi\n    fi\n\n    lock_delete $lockname \"$lock\"\n    rc=$?\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' released by '$requester'\"\n        return 0\n    else\n        log \"CIB deletion error, lock cannot be released\"\n        return 3\n    fi\n}\n\n\n# Retrieve the owner of a lock from the CIB\n# this is a read-only operation, so no need to log debug info\nlock_get_owner() {\n    local lockname=$1\n    local rc\n    local lock\n    local owner\n\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        return 1\n    else\n        lock_owner $lock\n        return 0\n    fi\n}\n\n\nACTION=$1\nLOCKNAME=$2\nREQUESTER=$3\nTTL=${4:-60}\n\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nif [ $ACTION != \"--help\" ]; then\n    if [ -z \"$LOCKNAME\" ]; then\n        error \"You must specific a lock name\"\n    fi\n    if [ $ACTION != \"--owner\" ] && [ $ACTION != \"-o\" ]; then\n        if [ -z \"$REQUESTER\" ]; then\n            error \"You must specific a lock requester\"\n        fi\n    fi\nfi\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) try_action lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --release|-r) try_action lock_release $LOCKNAME $REQUESTER;;\n    --acquire-once|-A) lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --owner|-o) lock_get_owner $LOCKNAME;;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "pacemaker_mutex_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_restart_bundle.sh --lock mysql galera galera-bundle Master _\n# pacemaker_mutex_restart_bundle.sh --lock ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\n\nset -u\n\nusage() {\n    echo \"Restart a clustered resource in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --lock <tripleo-service> <pcmk-resource> <pcmk-bundle> <target-state-local> <target-state-cluster>\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\nACTION=$1\ncase $ACTION in\n    --help) usage; exit 0;;\n    --lock) ;;\n    *) error \"Unknown action '$ACTION'\";;\nesac\n\nTRIPLEO_SERVICE=$2\nLOCK_NAME=${TRIPLEO_SERVICE}-restart-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to restart resource $TRIPLEO_SERVICE\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\nRESOURCE_NAME=$3\nBUNDLE_NAME=$4\nWAIT_TARGET_LOCAL=$5\nWAIT_TARGET_ANYWHERE=${6:-_}\n\nif pacemaker_supports_promoted; then\n    WAIT_TARGET_LOCAL=$(echo \"$5\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    WAIT_TARGET_ANYWHERE=$(echo \"${6:-_}\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    promoted_role=\"Promoted\"\nelse\n    promoted_role=\"Master\"\nfi\n\n# The lock TTL should accomodate for the resource start/promote timeout\nif [ \"$RESOURCE_NAME\" != \"$BUNDLE_NAME\" ]; then\n    if [ \"$WAIT_TARGET_LOCAL\" = \"$promoted_role\" ] || [ \"$WAIT_TARGET_ANYWHERE\" = \"$promoted_role\" ]; then\n        rsc_op=\"promote\"\n    else\n        rsc_op=\"start\"\n    fi\n    # <op id=\"galera-promote-interval-0s\" interval=\"0s\" name=\"promote\" on-fail=\"block\" timeout=\"300s\"/>\n    PCMK_TTL=$(cibadmin -Q | xmllint -xpath \"string(//primitive[@id='${RESOURCE_NAME}']/operations/op[@name='${rsc_op}']/@timeout)\" - | sed 's/s$//')\n    LOCK_TTL=$((PCMK_TTL + 30))\nelse\n    # The podman RA's default start timeout\n    LOCK_TTL=90\nfi\n\nlog \"Acquire a ${LOCK_TTL}s restart lock for service $TRIPLEO_SERVICE before restarting it\"\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nrc=1\nwhile [ $rc -ne 0 ]; do\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire $LOCK_NAME $LOCK_OWNER $LOCK_TTL\n    rc=$?\n    if [ $rc != 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not acquire lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Could not acquire lock, retrying\"\n            sleep 10\n        fi\n    fi\ndone\n\nlog \"Restart the service $TRIPLEO_SERVICE locally\"\n# Reuse the local restart script in t-h-t (driven by env var TRIPLEO_MINOR_UPDATE)\nTRIPLEO_MINOR_UPDATE=true /var/lib/container-config-scripts/pacemaker_restart_bundle.sh $TRIPLEO_SERVICE $RESOURCE_NAME $BUNDLE_NAME $WAIT_TARGET_LOCAL $WAIT_TARGET_ANYWHERE\n\n# If we reached this point, always try to release the lock\nlog \"Release the restart lock for service $TRIPLEO_SERVICE\"\n/var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $LOCK_NAME $LOCK_OWNER\nrc=$?\nif [ $rc -ne 0 ] && [ $rc -ne 1 ]; then\n    error \"Could not release held lock (rc: $rc)\"\nfi\n"}, "pacemaker_mutex_shutdown.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_shutdown.sh --acquire\n# pacemaker_mutex_shutdown.sh --release\n\nset -u\n\nusage() {\n    echo \"Shutdown a cluster node in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --acquire # prevent other node from shutting down until we hold the lock\"\n    echo \"   $0 --release # release the lock, other node can compete for the shutdown lock\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nshutdown_lock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc=1\n    local current_owner\n    local owner_stopped\n    local owner_rc\n\n    log \"Acquiring the shutdown lock\"\n    while [ $rc -ne 0 ]; do\n        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire-once $lockname $requester $ttl\n        rc=$?\n        if [ $rc -ne 0 ]; then\n            if [ $rc -eq 2 ]; then\n                error \"Could not acquire the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n            else\n                # The lock is held by another node.\n                current_owner=$(/var/lib/container-config-scripts/pacemaker_resource_lock.sh --owner $lockname)\n                owner_rc=$?\n                if [ $owner_rc -eq 2 ]; then\n                    error \"Could not get the shutdown lock owner due to unrecoverable error (rc: $owner_rc), bailing out\"\n                fi\n                if [ $owner_rc -eq 0 ]; then\n                    # If the owner is marked as offline, that means it has shutdown and\n                    # we can clean the lock preemptively and try to acquire it.\n                    owner_stopped=$(crm_mon -1X | xmllint --xpath 'count(//nodes/node[@name=\"'${current_owner}'\" and @online=\"false\" and @unclean=\"false\"])' -)\n                    if [ \"${owner_stopped}\" = \"1\" ]; then\n                        log \"Shutdown lock held by stopped node '${current_owner}', lock can be released\"\n                        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $current_owner\n                        continue\n                    fi\n                fi\n                log \"Shutdown lock held by another node (rc: $rc), retrying\"\n                sleep 10\n            fi\n        fi\n    done\n    log \"Shutdown lock acquired\"\n    return 0\n}\n\n\n# Release the lock if we still own it. Not owning it anymore is not fatal\nshutdown_lock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n\n    log \"Releasing the shutdown lock\"\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $requester\n    rc=$?\n    if [ $rc -ne 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not release the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Shutdown lock no longer held, nothing to do\"\n        fi\n    else\n        log \"Shutdown lock released\"\n    fi\n    return 0\n}\n\n\nACTION=$1\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nLOCK_NAME=tripleo-shutdown-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to aquire the shutdown lock\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\n# We start with a very high TTL, that long enough to accomodate a cluster stop.\n# As soon as the node will get offline, the other competing node will be entitled\n# to steal the lock, so they should never wait that long in practice.\nLOCK_TTL=600\n\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) shutdown_lock_acquire ${LOCK_NAME} ${LOCK_OWNER} ${LOCK_TTL};;\n    --release|-r) shutdown_lock_release ${LOCK_NAME} ${LOCK_OWNER};;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}}, "docker_config": {"step_2": {"create_dnsmasq_wrapper": {"start_order": 1, "detach": false, "net": "host", "pid": "host", "user": "root", "command": ["/container_puppet_apply.sh", "4", "file", "include ::tripleo::profile::base::neutron::dhcp_agent_wrappers"], "image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/var/lib/container-config-scripts/container_puppet_apply.sh:/container_puppet_apply.sh:ro", "/etc/puppet:/tmp/puppet-etc:ro", "/usr/share/openstack-puppet/modules:/usr/share/openstack-puppet/modules:ro", "/run/openvswitch:/run/openvswitch:shared,z", "/var/lib/neutron:/var/lib/neutron:shared,z"]}}, "step_4": {"neutron_dhcp": {"start_order": 10, "image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "net": "host", "pid": "host", "cgroupns": "host", "privileged": true, "restart": "always", "security_opt": ["label=disable"], "depends_on": ["openvswitch.service"], "healthcheck": {"test": "/openstack/healthcheck 5672"}, "ulimit": ["nofile=16384"], "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/neutron:/var/log/neutron:z", "/var/lib/kolla/config_files/neutron_dhcp.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/neutron:/var/lib/kolla/config_files/src:ro", "/lib/modules:/lib/modules:ro", "/run/openvswitch:/run/openvswitch:shared,z", "/var/lib/neutron:/var/lib/neutron:shared,z", "/run/netns:/run/netns:shared", "/var/lib/neutron/kill_scripts:/etc/neutron/kill_scripts:shared,z", "/var/lib/neutron/dnsmasq_wrapper:/usr/local/bin/dnsmasq:ro", "/var/lib/neutron/dhcp_haproxy_wrapper:/usr/local/bin/haproxy:ro", "/etc/pki/tls/certs/neutron.crt:/var/lib/kolla/config_files/src-tls/etc/pki/tls/certs/neutron.crt:ro", "/etc/pki/tls/private/neutron.key:/var/lib/kolla/config_files/src-tls/etc/pki/tls/private/neutron.key:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "metadata_settings": [{"service": "neutron", "network": "internal_api", "type": "node"}], "deploy_steps_tasks": [{"name": "Certificate generation", "when": ["step|int == 1", "enable_internal_tls"], "block": [{"include_role": {"name": "linux-system-roles.certificate"}, "vars": {"certificate_requests": [{"name": "neutron", "dns": "{{fqdn_internal_api}}", "principal": "neutron/{{fqdn_internal_api}}@{{idm_realm}}", "run_after": "container_name=$({{container_cli}} ps --format=\\{\\{.Names\\}\\} | grep neutron_dhcp)\n# The certificate is also installed on the computes, but neutron_dhcp is only\n# present on the controllers, so we exit if the container could not be found.\n[[ -z $container_name ]] && exit 0\n\nservice_crt=\"/etc/pki/tls/certs/neutron.crt\"\nservice_key=\"/etc/pki/tls/private/neutron.key\"\n# Copy the new cert from the mount-point to the real path\n{{container_cli}} exec -u root \"$container_name\" cp \"/var/lib/kolla/config_files/src-tls$service_crt\" \"$service_crt\"\n# Copy the new key from the mount-point to the real path\n{{container_cli}} exec -u root \"$container_name\" cp \"/var/lib/kolla/config_files/src-tls$service_key \"$service_key\"\n# No need to trigger a reload for neutron dhcpd since the cert is not cached\n", "key_size": "2048", "ca": "ipa"}]}}]}], "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "state": "directory", "setype": "{{ item.setype }}", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/neutron", "setype": "container_file_t", "mode": "0750"}]}, {"name": "create /run/netns with temp namespace", "command": "ip netns add ns_temp", "register": "ipnetns_add_result", "failed_when": false}, {"name": "remove temp namespace", "command": "ip netns delete ns_temp", "failed_when": false, "when": ["ipnetns_add_result.rc is defined", "ipnetns_add_result.rc == 0"]}, {"name": "create /var/lib/neutron", "file": {"path": "/var/lib/neutron", "state": "directory", "setype": "container_file_t"}}, {"name": "enable virt_sandbox_use_netlink for healthcheck", "seboolean": {"name": "virt_sandbox_use_netlink", "persistent": true, "state": true}, "when": ["ansible_facts.selinux is defined", "ansible_facts.selinux.status == \"enabled\""]}, {"name": "set conditions", "set_fact": {"dnsmasq_wrapper_enabled": true, "haproxy_wrapper_enabled": true, "debug_enabled": true, "docker_additional_sockets": ["/var/lib/openstack/docker.sock"]}}, {"name": "create kill_scripts directory within /var/lib/neutron", "file": {"state": "directory", "path": "/var/lib/neutron/kill_scripts"}}, {"name": "create dnsmasq dhcp kill script", "when": "dnsmasq_wrapper_enabled|bool", "copy": {"dest": "/var/lib/neutron/kill_scripts/dnsmasq-kill", "mode": 493, "content": "#!/bin/bash\n{% if debug_enabled|bool -%}\nset -x\n{% endif -%}\nadd_date() {\n  echo \"$(date) $@\"\n}\n\n# Set up script logging for debugging purpose.\n# It will be taken care of by logrotate since there is the .log\n# suffix.\nexec 3>&1 4>&2\ntrap 'exec 2>&4 1>&3' 0 1 2 3\nexec 1>>/var/log/neutron/kill-script.log 2>&1\n\nSIG=$1\nPID=$2\nNETNS=$(ip netns identify ${PID})\n\nif [ \"x${NETNS}\" == \"x\" ]; then\n  CLI=\"nsenter --all --preserve-credentials -t 1 podman\"\n  SIG=9\nelse\n  CLI=\"nsenter --net=/run/netns/${NETNS} --preserve-credentials -m -t 1 podman\"\nfi\n\nkill_container() {\n  add_date \"Killing container $1 ($2)\"\n  signal_container $1 $2 9\n}\n\nstop_container() {\n  add_date \"Stopping container $1 ($2)\"\n  $CLI stop $2\n}\n\nsignal_container() {\n  SIGNAL=$3\n  if [ -z \"$SIGNAL\" ]; then\n      SIGNAL=\"HUP\"\n  fi\n  add_date \"Sending signal '$SIGNAL' to $1 ($2)\"\n  $CLI kill --signal $SIGNAL $2\n}\n\ndelete_container() {\n  add_date \"Deleting container $1 ($2)\"\n  $CLI rm $2 || echo \"Deleting container $1 ($2) failed\"\n}\n\n\n{% raw -%}\nif [ -f /proc/$PID/cgroup ]; then\n  # Get container ID based on process cgroups\n  CT_ID=$(awk 'BEGIN {FS=\".scope|-\"} /\\/libpod-/ {if ($(NF-1)) print $(NF-1);exit}' /proc/$PID/cgroup)\n  CT_NAME=$($CLI inspect -f '{{.Name}}' $CT_ID)\n\n  case $SIG in\n    HUP)\n      signal_container $CT_NAME $CT_ID\n      ;;\n    9)\n      kill_container $CT_NAME $CT_ID\n      delete_container $CT_NAME $CT_ID\n      ;;\n    15)\n      stop_container $CT_NAME $CT_ID\n      delete_container $CT_NAME $CT_ID\n      ;;\n    *)\n      add_date \"Unknown action ${SIG} for ${CT_NAME} ${CT_ID}\"\n      exit 1\n      ;;\n  esac\n\nelse\n  add_date \"No such PID: ${PID}\"\n  exit 1\nfi\n{% endraw %}\n"}}, {"name": "create haproxy kill script", "when": "haproxy_wrapper_enabled|bool", "copy": {"dest": "/var/lib/neutron/kill_scripts/haproxy-kill", "mode": 493, "content": "#!/bin/bash\n{% if debug_enabled|bool -%}\nset -x\n{% endif -%}\nadd_date() {\n  echo \"$(date) $@\"\n}\n\n# Set up script logging for debugging purpose.\n# It will be taken care of by logrotate since there is the .log\n# suffix.\nexec 3>&1 4>&2\ntrap 'exec 2>&4 1>&3' 0 1 2 3\nexec 1>>/var/log/neutron/kill-script.log 2>&1\n\nSIG=$1\nPID=$2\nNETNS=$(ip netns identify ${PID})\n\nif [ \"x${NETNS}\" == \"x\" ]; then\n  CLI=\"nsenter --all --preserve-credentials -t 1 podman\"\n  SIG=9\nelse\n  CLI=\"nsenter --net=/run/netns/${NETNS} --preserve-credentials -m -t 1 podman\"\nfi\n\nkill_container() {\n  add_date \"Killing container $1 ($2)\"\n  signal_container $1 $2 9\n}\n\nstop_container() {\n  add_date \"Stopping container $1 ($2)\"\n  $CLI stop $2\n}\n\nsignal_container() {\n  SIGNAL=$3\n  if [ -z \"$SIGNAL\" ]; then\n      SIGNAL=\"HUP\"\n  fi\n  add_date \"Sending signal '$SIGNAL' to $1 ($2)\"\n  $CLI kill --signal $SIGNAL $2\n}\n\ndelete_container() {\n  add_date \"Deleting container $1 ($2)\"\n  $CLI rm $2 || echo \"Deleting container $1 ($2) failed\"\n}\n\n\n{% raw -%}\nif [ -f /proc/$PID/cgroup ]; then\n  # Get container ID based on process cgroups\n  CT_ID=$(awk 'BEGIN {FS=\".scope|-\"} /\\/libpod-/ {if ($(NF-1)) print $(NF-1);exit}' /proc/$PID/cgroup)\n  CT_NAME=$($CLI inspect -f '{{.Name}}' $CT_ID)\n\n  case $SIG in\n    HUP)\n      signal_container $CT_NAME $CT_ID\n      ;;\n    9)\n      kill_container $CT_NAME $CT_ID\n      delete_container $CT_NAME $CT_ID\n      ;;\n    15)\n      stop_container $CT_NAME $CT_ID\n      delete_container $CT_NAME $CT_ID\n      ;;\n    *)\n      add_date \"Unknown action ${SIG} for ${CT_NAME} ${CT_ID}\"\n      exit 1\n      ;;\n  esac\n\nelse\n  add_date \"No such PID: ${PID}\"\n  exit 1\nfi\n{% endraw %}\n"}}], "upgrade_tasks": []}, "tuple:('role_data', 'service_names')": null, "tuple:('role_data', 'config_settings')": {"neutron::notification_driver": ["noop"], "neutron::core_plugin": "ml2", "neutron::service_plugins": ["qos", "ovn-router", "trunk", "segments", "port_forwarding", "log"], "neutron::report_interval": 300, "neutron::logging::debug": true, "neutron::purge_config": false, "neutron::allow_overlapping_ips": true, "neutron::dhcp_agent_notification": true, "neutron::dns_domain": "ooo.test", "neutron::rabbit_heartbeat_timeout_threshold": 60, "neutron::host": "%{hiera('fqdn_canonical')}", "neutron::db::database_db_max_retries": -1, "neutron::db::database_max_retries": -1, "neutron::db::sync::db_sync_timeout": 900, "neutron::global_physnet_mtu": 1500, "neutron::db::sync::extra_params": "", "neutron::vlan_transparent": false, "neutron::rabbit_heartbeat_in_pthread": false, "tripleo::profile::base::neutron::dhcp_agent_wrappers::enable_dnsmasq_wrapper": true, "tripleo::profile::base::neutron::dhcp_agent_wrappers::dnsmasq_process_wrapper": "/var/lib/neutron/dnsmasq_wrapper", "tripleo::profile::base::neutron::dhcp_agent_wrappers::dnsmasq_image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "tripleo::profile::base::neutron::dhcp_agent_wrappers::enable_haproxy_wrapper": true, "tripleo::profile::base::neutron::dhcp_agent_wrappers::haproxy_process_wrapper": "/var/lib/neutron/dhcp_haproxy_wrapper", "tripleo::profile::base::neutron::dhcp_agent_wrappers::haproxy_image": "registry.redhat.io/rhosp-rhel9/openstack-neutron-dhcp-agent:17.1", "tripleo::profile::base::neutron::dhcp_agent_wrappers::debug": true, "tripleo::profile::base::neutron::container_cli": "podman", "neutron::agents::dhcp::enable_isolated_metadata": false, "neutron::agents::dhcp::enable_force_metadata": true, "neutron::agents::dhcp::enable_metadata_network": false, "neutron::agents::dhcp::dnsmasq_local_resolv": false, "neutron::agents::dhcp::dnsmasq_dns_servers": [], "neutron::agents::dhcp::interface_driver": "neutron.agent.linux.interface.OVSInterfaceDriver", "neutron::agents::dhcp::dhcp_broadcast_reply": false, "neutron::agents::dhcp::dnsmasq_enable_addr6_list": true, "neutron::agents::dhcp::debug": true, "neutron::agents::dhcp::ovsdb_agent_ssl_key_file": "/etc/pki/tls/private/neutron.key", "neutron::agents::dhcp::ovsdb_agent_ssl_cert_file": "/etc/pki/tls/certs/neutron.crt", "neutron::agents::dhcp::ovsdb_agent_ssl_ca_file": "/etc/ipa/ca.crt"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-33-ol5oesruk6ik/b16f3c38-e31b-4c1f-8637-2f23f15d97b2"}


created_at: 2026-06-13 07:32:00.206304
data:
  {"type": "json", "value": {"ContainerNovaApiImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:04.132063
data:
  {"type": "json", "value": {"ContainerNovaApiImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "ContainerNovaConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:09.199601
data:
  {"container_config_scripts": {"container_puppet_apply.sh": {"mode": "0700", "content": "#!/bin/bash\nset -eux\nSTEP=$1\nTAGS=$2\nCONFIG=$3\nEXTRA_ARGS=${4:-''}\nif [ -d /tmp/puppet-etc ]; then\n  # ignore copy failures as these may be the same file depending on docker mounts\n  cp -a /tmp/puppet-etc/* /etc/puppet || true\nfi\necho \"{\\\"step\\\": ${STEP}}\" > /etc/puppet/hieradata/docker_puppet.json\n# $::deployment_type in puppet-tripleo\nexport FACTER_deployment_type=containers\nset +e\npuppet apply $EXTRA_ARGS \\\n    --verbose \\\n    --detailed-exitcodes \\\n    --summarize \\\n    --color=false \\\n    --modulepath /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules \\\n    --tags $TAGS \\\n    -e \"noop_resource('package'); ${CONFIG}\"\nrc=$?\nset -e\nset +ux\nif [ $rc -eq 2 -o $rc -eq 0 ]; then\n    exit 0\nfi\nexit $rc\n"}, "pyshim.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n#\n# Usage: pyshim.sh <script and/or arguments>\n#\n# Unfortunately THT doesn't know which version of python might be in a\n# container so we need this script to be able to try python3 or python2\n# depending on availability.  Since this is a temporary shim until we've\n# fully cut over to python3, we check for the existence of python3 first\n# before falling back to python2. This will help in the transition from\n# python2 based containers to python3.\n\nshow_usage() {\n    echo \"Usage: pyshim.sh <script and/or arguments>\"\n}\n\nif [ $# -lt 1 ]\nthen\n    show_usage\n    exit 1\nfi\n\nset -x\nif command -v python3 >/dev/null; then\n    python3 \"$@\"\nelif command -v python2 >/dev/null; then\n    python2 \"$@\"\nelif command -v python >/dev/null; then\n    python \"$@\"\nelse\n    echo \"ERROR: python is not available!\"\n    exit 1\nfi\n"}, "pacemaker_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nset -u\n\n# ./pacemaker_restart_bundle.sh mysql galera galera-bundle Master _\n# ./pacemaker_restart_bundle.sh redis redis redis-bundle Slave Master\n# ./pacemaker_restart_bundle.sh ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\nRESTART_SCRIPTS_DIR=$(dirname $0)\nTRIPLEO_SERVICE=$1\nRESOURCE_NAME=$2\nBUNDLE_NAME=$3\nWAIT_TARGET_LOCAL=$4\nWAIT_TARGET_ANYWHERE=${5:-_}\nTRIPLEO_MINOR_UPDATE=\"${TRIPLEO_MINOR_UPDATE:-false}\"\nTRIPLEO_HA_WRAPPER_RESOURCE_EXISTS=\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS:-false}\"\n\nbundle_can_be_restarted() {\n    local bundle=$1\n    # As long as the resource bundle is managed by pacemaker and is\n    # not meant to stay stopped, no matter the state of any inner\n    # pcmk_remote or ocf resource, we should restart it to give it a\n    # chance to read the new config.\n    [ \"$(crm_resource --meta -r $1 -g is-managed 2>/dev/null)\" != \"false\" ] && \\\n    [ \"$(crm_resource --meta -r $1 -g target-role 2>/dev/null)\" != \"Stopped\" ]\n}\n\nlog() {\n    local msg=$1\n    logger -t pcmkrestart \"$1\"\n}\n\nHOSTNAME=$(/bin/hostname -s)\nif [ x\"${TRIPLEO_MINOR_UPDATE,,}\" != x\"true\" ]; then\n    if [ x\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS,,}\" = x\"false\" ]; then\n        # Do not restart during initial deployment, as the resource\n        # has just been created.\n        SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n        if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n            log \"Initial deployment, skipping the restart of ${BUNDLE_NAME}\"\n\tfi\n        exit 0\n    else\n        # During a stack update, this script is called in parallel on\n        # every node the resource runs on, after the service's configs\n        # have been updated on all nodes. So we need to run pcs only\n        # once (e.g. on the service's boostrap node).\n        if bundle_can_be_restarted ${BUNDLE_NAME}; then\n            SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n            if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n                replicas_running=$(crm_resource -Q -r $BUNDLE_NAME --locate 2>&1 | wc -l)\n                if [ \"$replicas_running\" != \"0\" ]; then\n                    log \"Restarting ${BUNDLE_NAME} globally. Stopping:\"\n                    /sbin/pcs resource disable --wait=600 $BUNDLE_NAME\n                    log \"Restarting ${BUNDLE_NAME} globally. Starting:\"\n                    /sbin/pcs resource enable --wait=600 $BUNDLE_NAME\n                else\n                    log \"${BUNDLE_NAME} is not running anywhere,\" \\\n                         \"cleaning up to restart it globally if necessary\"\n                    /sbin/pcs resource cleanup $BUNDLE_NAME\n                fi\n            else\n                log \"Skipping global restart of ${BUNDLE_NAME} on ${HOSTNAME} it will be restarted by node ${SERVICE_NODEID}\"\n            fi\n\n        else\n            log \"No global restart needed for ${BUNDLE_NAME}.\"\n        fi\n    fi\nelse\n    # During a minor update workflow however, a host gets fully\n    # updated before updating the next one. So unlike stack\n    # update, at the time this script is called, the service's\n    # configs aren't updated on all nodes yet. So only restart the\n    # resource locally, where it's guaranteed that the config is\n    # up to date.\n    HOST=$(facter hostname)\n\n    if bundle_can_be_restarted ${BUNDLE_NAME}; then\n\t# if the resource is running locally, restart it\n\tif crm_resource -r $BUNDLE_NAME --locate 2>&1 | grep -w -q \"${HOST}\"; then\n            log \"Restarting ${BUNDLE_NAME} locally on '${HOST}'\"\n            /sbin/pcs resource restart $BUNDLE_NAME \"${HOST}\"\n\n\telse\n\t    # At this point, if no resource is running locally, it's\n\t    # either because a) it has failed previously, or b) because\n\t    # it's an A/P resource running elsewhere.\n\t    # By cleaning up resource, we ensure that a) it will try to\n\t    # restart, or b) it won't do anything if the resource is\n\t    # already running elsewhere.\n            log \"${BUNDLE_NAME} is currently not running on '${HOST}',\" \\\n                 \"cleaning up its state to restart it if necessary\"\n            /sbin/pcs resource cleanup $BUNDLE_NAME node=\"${HOST}\"\n\tfi\n\n\t# Wait until the resource is in the expected target state\n\t$RESTART_SCRIPTS_DIR/pacemaker_wait_bundle.sh \\\n            $RESOURCE_NAME $BUNDLE_NAME \\\n            \"$WAIT_TARGET_LOCAL\" \"$WAIT_TARGET_ANYWHERE\" \\\n\t    \"${HOST}\" 600\n    else\n        log \"No restart needed for ${BUNDLE_NAME}.\"\n    fi\nfi\n"}, "pacemaker_wait_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# ----\n# Wait for an OCF resource or a bundle to be restarted\n# ----\n# e.g.:\n# M/S OCF:      $0 galera galera-bundle Master\n# clone OCF:    $0 rabbitmq rabbitmq-bundle Started\n# A/P M/S OCF:  $0 redis redis-bundle Slave Master\n# A/P bundle:   $0 openstack-cinder-volume openstack-cinder-volume _ Started\n# clone bundle: $0 haproxy-bundle haproxy-bundle Started\n\n# design note 1:\n#  - this script is called during a minor update; it is called\n#    once per node that hosts a service replica.\n#  - the purpose of this script is to ensure that restarting the\n#    service replica locally won't disrupt the service availability\n#    for the end user. To reach that goal, the script waits until the\n#    service is restarted locally or globallu and reaches a given\n#    target state (i.e. Started, Slave or Master).\n# design note 2:\n#   - we don't want to track restart error: our only job is to ensure\n#     service restart synchronization, not service health.\n#   - In particular, we don't want to error out in case the resource\n#     cannot be restarted locally, because that would make the minor\n#     update fail, even if potentially other replicas still provide\n#     the service.\n# design note 3:\n#   - we can bail out early if we determine that the resource can't\n#     be restarted automatically by pacemaker (e.g. its \"blocked\",\n#     unmanaged or disabled).\n\nlog() {\n    local msg=$1\n    echo \"$(date -u): $1\"\n}\n\nusage() {\n    echo 2>&1 \"Usage: $0 NAME BUNDLE_NAME ROLE_LOCAL [ROLE_ANYWHERE] [HOST] [TIMEOUT]\"\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\n#\n# Utility functions to detect stuck resources\n#\n\nbundle_failures_locally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local replica_name\n    for i in $(seq 0 $last); do\n\treplica_name=${BUNDLE_NAME}-${engine}-${i}\n\tcrm_failcount -q -G -r $replica_name -N $HOST\n    done\n}\n\nbundle_failures_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    for i in $(seq 0 $last); do\n\tcrm_failcount -q -G -r ${BUNDLE_NAME}-${engine}-${i}\n    done\n}\n\nbundle_running_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    # return the number of running bundles replica, i.e. the number of\n    # docker/podman resource replicas currently running in the cluster\n    crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:heartbeat:${engine}']/node)\" -\n}\n\nocf_failures_globally() {\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local bundle_node\n    for i in $(seq 0 $last); do\n\tbundle_node=${BUNDLE_NAME}-${i}\n\tcrm_failcount -q -G -r $NAME -N $bundle_node\n    done\n}\n\ndid_resource_failed_locally() {\n    local failures\n    local running\n    local remotehost\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# if we're dealing with an ocf resource, it is running on a\n\t# pacemaker_remote rather that on the real host, and the\n\t# failcounts are thus associated to the pcmk remote. Replace\n\t# the host's name with the pcmk remote's name.\n        remotehost=$(crm_mon --as-xml | xmllint --xpath \"string(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource/node[@name='${HOST}']/../../resource[@resource_agent='${OCF}:pacemaker:remote']/@id)\" -)\n\tif [ -n \"${remotehost}\" ]; then\n\t    crm_failcount -q -G -r $NAME -N $remotehost | grep -q -w INFINITY\n\t    return $?\n\tfi\n\t# If no pcmk remote is currently running, the failcount from\n\t# the ocf resource is useless, compute the failcount from the\n\t# bundle case instead (computed below).\n    fi\n\n    # for bundles, pacemaker can run any bundle replica locally\n    # (e.g. galera-bundle-docker-{0,1,2}), and a failure happens when\n    # there are no more replica to try.\n    # That is, when _at least_ one replica failed locally, and all the\n    # others either failed or are currently running elsewhere.\n    failures=$(bundle_failures_locally $HOST | grep -c -w INFINITY)\n    running=$(bundle_running_globally)\n    test $failures -gt 0 && \\\n    test $(( $failures + $running )) -ge $BUNDLE_REPLICAS\n}\n\ndid_resource_failed_globally() {\n    local remotecount\n    local failures\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# we check the state of an ocf resource only if the\n\t# pcmkremotes are started\n        remotecount=$(crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node)\" -)\n\tif [ \"${remotecount}\" = \"0\" ]; then\n\t    # no pcmkremote is running, so check the bundle state\n\t    # instead of checking the ocf resource\n\t    # bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\t    failures=$(bundle_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\telse\n\t    # ocf resource failed if it failed to start on\n\t    # all $BUNDLE_REPLICAS bundle nodes\n\t    failures=$(ocf_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\tfi\n    else\n\t# bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\tfailures=$(bundle_failures_globally | grep -c -w INFINITY)\n\ttest $failures -eq $BUNDLE_REPLICAS\n    fi\n}\n\n\n# Input validation\n#\n\nNAME=$1\nif [ -z \"${NAME}\" ]; then\n    echo 2>&1 \"Error: argument NAME must not be empty\"\n    exit 1\nfi\n\nBUNDLE_NAME=$2\nif [ -z \"${BUNDLE_NAME}\" ]; then\n    echo 2>&1 \"Error: argument BUNDLE_NAME must not be empty\"\n    exit 1\nfi\n\nROLE_LOCAL=$3\nif [ \"${ROLE_LOCAL}\" = \"_\" ]; then\n    ROLE_LOCAL=\"\"\nfi\n\nROLE_ANYWHERE=$4\nif [ \"${ROLE_ANYWHERE}\" = \"_\" ]; then\n    ROLE_ANYWHERE=\"\"\nfi\n\nif [ -z \"${ROLE_LOCAL}\" ]; then\n    if [ -z \"${ROLE_ANYWHERE}\" ]; then\n        echo 2>&1 \"Error: either ROLE_LOCAL or ROLE_ANYWHERE must be non empty\"\n        exit 1\n    fi\nelse\n    if !(echo \"${ROLE_LOCAL}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n        echo 2>&1 \"Error: argument ROLE_LOCAL must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n        exit 1\n    fi\nfi\n\nif [ -n \"${ROLE_ANYWHERE}\" ] && !(echo \"${ROLE_ANYWHERE}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n    echo 2>&1 \"Error: argument ROLE_ANYWHERE must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n    exit 1\nfi\n\n# Ensure compatibility with pacemaker 2.1\nif pacemaker_supports_promoted; then\n    ROLE_LOCAL=$(echo \"$ROLE_LOCAL\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    ROLE_ANYWHERE=$(echo \"$ROLE_ANYWHERE\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    OCF=\"ocf\"\nelse\n    OCF=\"ocf:\"\nfi\n\nHOST=${5:-$(facter hostname)}\nTIMEOUT=${6:-600}\n\n\n# Configure the search\n# ----\n# Note: we can't use crm_resource in all searches because we can't\n# easily extract the host the OCF resources run on (crm_resource\n# returns the pcmk-remote nodes rather than the hosts)\n# So instead, we implement various searches with XPath directly.\n\nif [ \"${BUNDLE_NAME}\" != \"${NAME}\" ]; then\n# ocf resource\nlocal_resource_xpath=\"//bundle/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node[@name='${HOST}']/../../resource[@id='${NAME}']\"\nany_resource_xpath=\"//bundle//resource[@id='${NAME}']\"\nreplicas_xpath=\"//bundle/primitive[@id='${BUNDLE_NAME}']/../*[boolean(@image) and boolean(@replicas)]\"\nelse\n# bundle resource\nlocal_resource_xpath=\"//bundle[@id='${NAME}']/replica/resource/node[@name='${HOST}']/../../resource\"\nany_resource_xpath=\"//bundle[@id='${NAME}']//resource\"\nreplicas_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nfi\n\nbundle_def_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nBUNDLE_CONTAINER_ENGINE=$(cibadmin -Q | xmllint --xpath \"name(${bundle_def_xpath})\" -)\nBUNDLE_REPLICAS=$(cibadmin -Q | xmllint --xpath \"string(${bundle_def_xpath}/@replicas)\" -)\n\n\n# The wait algorithm follows a two-stage approach\n#  1. Depending on how the script is called, we first check whether\n#     the resource is restarted locally. An A/P resource may be\n#     restarted elsewhere in the cluster.\n#  2. If needed, check whether the A/P resource has restarted\n#     elsewhere. For A/P M/S resources, in case the resource is\n#     restarted as Slave locally, ensure a Master is available.\n\nsuccess=1\nbailout=1\ntimeout=$TIMEOUT\nrole=\"\"\n\n# Stage 1: local check\nif [ -n \"$ROLE_LOCAL\" ]; then\n    log \"Waiting until ${NAME} has restarted on ${HOST} and is in state ${ROLE_LOCAL}\"\n    log \"Will probe resource state with the following XPath pattern: ${local_resource_xpath}\"\n\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resource=$(crm_mon -r --as-xml | xmllint --xpath \"${local_resource_xpath}\" - 2>/dev/null)\n        role=$(echo \"${resource}\" | sed -ne 's/.*\\Wrole=\"\\([^\"]*\\)\".*/\\1/p')\n\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resource}\" | grep -q -w \"\\Wblocked=\\\"true\\\"\"; then\n            log \"${NAME} is blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_locally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif [ \"$role\" = \"$ROLE_LOCAL\" ]; then\n            success=0\n            continue\n        elif [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" = \"$ROLE_ANYWHERE\" ]; then\n            # A/P: we are restarted in the expected state\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_LOCAL} on ${HOST}\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\n# Stage 2: global check\nif [ $timeout -gt 0 ] && [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" != \"$ROLE_ANYWHERE\" ]; then\n    log \"Wait until ${NAME} is restarted anywhere in the cluster in state ${ROLE_ANYWHERE}\"\n    log \"Will probe resource state with the following XPath pattern: ${any_resource_xpath}\"\n\n    success=1\n    bailout=1\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resources=$(crm_mon -r --as-xml | xmllint --xpath \"${any_resource_xpath}\" - 2>/dev/null)\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif ! (echo \"${resources}\" | grep -q -w \"\\Wblocked=\\\"false\\\"\"); then\n            log \"${NAME} blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_globally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resources}\" | grep -q -w \"\\Wrole=\\\"${ROLE_ANYWHERE}\\\"\"; then\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_ANYWHERE} anywhere in the cluster\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\nif [ $timeout -le 0 ]; then\n    log \"Timeout reached after ${TIMEOUT}s while waiting for ${NAME} to be restarted\"\nelif [ $bailout -le 0 ]; then\n    log \"Restart monitoring for ${NAME} cancelled\"\nfi\n\nif [ $success -eq 0 ]; then\n    log \"${NAME} successfully restarted\"\nelse\n    log \"${NAME} was not restarted properly\"\nfi\n\n# Don't block minor update or stack update if the wait was unsuccessful\nexit 0\n"}, "wait-port-and-run.sh": {"mode": "0755", "content": "#!/usr/bin/bash\nset -eu\n\nHOST=$1\nPORT=$2\n\necho \"$(date -u): Checking whether we can bind to ${HOST}:${PORT}\"\nwhile (ss -Htnl src \"${HOST}\" \"sport = :${PORT}\" | grep -wq \"${PORT}\"); do\n    echo \"$(date -u): ${HOST}:${PORT} still in use, waiting...\";\n    sleep 10;\ndone\n\nshift 2\nCOMMAND=\"$*\"\nif [ -z \"${COMMAND}\" ]; then\n    COMMAND=\"true\"\nfi\nexec $COMMAND\n"}, "pacemaker_resource_lock.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nMAX_RETRIES=10\nCIB_ENOTFOUND=105\n\nusage() {\n   echo \"Set a global property in the cluster with a validity timestamp.\"\n   echo \"Usage:\"\n   echo \"   $0 --acquire <lock_name> <lock_owner> <lock_ttl_in_seconds>\"\n   echo \"   $0 --release <lock_name> <lock_owner>\"\n   echo\n}\n\nlog() {\n    echo \"$(date -u): $1\" 1>&2\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\nlock_create() {\n    local name=$1\n    local data=$2\n    # cibadmin won't overwrite a key if someone else succeeded to create it concurrently\n    cibadmin --sync-call --scope crm_config --create --xml-text \"<cluster_property_set id='${name}'><nvpair id='${name}-pair' name='${name}' value='${data}'/></cluster_property_set>\" &>/dev/null\n    return $?\n}\n\nlock_update() {\n    local name=$1\n    local expected_data=$2\n    local new_data=$3\n    # we only update the lock we expect to see, so we can't update someone else's lock\n    cibadmin --sync-call --scope crm_config --modify --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" --xml-text \"<nvpair id='${name}-pair' name='${name}' value='${new_data}'/>\" &>/dev/null\n    return $?\n}\n\nlock_delete() {\n    local name=$1\n    local expected_data=$2\n    # we only delete the lock we expect to see, so we can't delete someone else's lock\n    cibadmin --sync-call --scope crm_config --delete --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" &>/dev/null\n    return $?\n}\n\nlock_get() {\n    local lockname=$1\n    local res\n    local rc\n    res=$(cibadmin --query --scope crm_config --xpath \"//cluster_property_set/nvpair[@name='$lockname']\" 2>/dev/null)\n    rc=$?\n    if [ $rc -eq 0 ]; then\n        echo \"$res\" | sed -n 's/.*value=\"\\([^\"]*\\)\".*/\\1/p'\n    fi\n    return $rc\n}\n\nlock_owner() {\n    local lock=$1\n    echo \"$lock\" | cut -d':' -f1\n}\n\nlock_has_expired() {\n    local lock=$1\n    local expiry=$(echo \"$lock\" | cut -d':' -f2)\n    local now=$(date +%s)\n    test $now -ge $expiry\n}\n\n\n# Perform a lock action and restart if the CIB has been modified before\n# committing the lock action\ntry_action() {\n    local fun=$1\n    local lock=$2\n    local requester=$3\n    local args=${4:-}\n    local tries=$MAX_RETRIES\n    local rc=1\n    if [ \"$fun\" = \"lock_acquire\" ] || [ \"$fun\" = \"lock_release\" ]; then\n        log \"Try running $fun\"\n    else\n        return 2\n    fi\n    while [ $rc -ne 0 ]; do\n        $fun $lock $requester $args\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Operation $1 succeeded\"\n            return 0\n        elif [ $rc -eq 3 ]; then\n            # rc == 3 -> CIB changed before push\n            if [ $tries -eq 0 ]; then\n                log \"Failed to commit after $MAX_RETRIES retries. Bailing out.\"\n                return 2\n            else\n                log \"Failed to commit. Retrying operation.\"\n                tries=$(($tries - 1))\n            fi\n        elif [ $rc -eq 2 ]; then\n            # rc == 2 -> unrecoverable cib error (e.g. pacemaker down)\n            log \"Unexpected failure. Bailing out\"\n            return $rc\n        else\n            # rc == 1 -> lock error (not owner, lock doesn't exists)\n            return $rc\n        fi\n    done\n}\n\n# The lock mechanism uses cibadmin's atomic creation so cluster-wide\n# state coherency is guaranteed by pacemaker\nlock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc\n    local lock\n    local expiry\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -n \"$lock\" ]; then\n        lock_has_expired $lock\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Lock has expired, now available for being held\"\n        else\n            # lock is still held. check whether we're the owner\n            owner=$(lock_owner $lock)\n            if [ \"$owner\" = \"$requester\" ];then\n                log \"Requester already owns the lock, acquiring attempt will just reconfigure the TTL\"\n            else\n                log \"Lock is held by someone else ($owner)\"\n                return 1\n            fi\n        fi\n    else\n        log \"Lock is not held yet\"\n    fi\n\n    # prepare the lock info\n    expiry=$(($(date +%s) + $ttl))\n\n    if [ -n \"$lock\" ]; then\n        log \"Attempting to update the lock\"\n        lock_update $lockname \"$lock\" \"$requester:$expiry\"\n        rc=$?\n    else\n        log \"Attempting to acquire the lock\"\n        lock_create $lockname \"$requester:$expiry\"\n        rc=$?\n    fi\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' acquired by '$requester', valid until $(date -d @$expiry)\"\n        return 0\n    else\n        log \"CIB changed, lock cannot be acquired\"\n        return 3\n    fi\n}\n\n\n# The lock mechanism uses the CIB's num_updates tag to implement\n# a conditional store. Cluster-wide locking is guaranteed by pacemaker\nlock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n    local lock\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        log \"Lock doesn't exist. Nothing to release\"\n        return 0\n    else\n        log \"Lock exists, check whether we're the owner\"\n        owner=$(lock_owner $lock)\n        if [ \"$owner\" != \"$requester\" ];then\n            log \"Lock is held by someone else ($owner), will not unlock\"\n            return 1\n        fi\n    fi\n\n    lock_delete $lockname \"$lock\"\n    rc=$?\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' released by '$requester'\"\n        return 0\n    else\n        log \"CIB deletion error, lock cannot be released\"\n        return 3\n    fi\n}\n\n\n# Retrieve the owner of a lock from the CIB\n# this is a read-only operation, so no need to log debug info\nlock_get_owner() {\n    local lockname=$1\n    local rc\n    local lock\n    local owner\n\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        return 1\n    else\n        lock_owner $lock\n        return 0\n    fi\n}\n\n\nACTION=$1\nLOCKNAME=$2\nREQUESTER=$3\nTTL=${4:-60}\n\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nif [ $ACTION != \"--help\" ]; then\n    if [ -z \"$LOCKNAME\" ]; then\n        error \"You must specific a lock name\"\n    fi\n    if [ $ACTION != \"--owner\" ] && [ $ACTION != \"-o\" ]; then\n        if [ -z \"$REQUESTER\" ]; then\n            error \"You must specific a lock requester\"\n        fi\n    fi\nfi\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) try_action lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --release|-r) try_action lock_release $LOCKNAME $REQUESTER;;\n    --acquire-once|-A) lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --owner|-o) lock_get_owner $LOCKNAME;;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "pacemaker_mutex_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_restart_bundle.sh --lock mysql galera galera-bundle Master _\n# pacemaker_mutex_restart_bundle.sh --lock ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\n\nset -u\n\nusage() {\n    echo \"Restart a clustered resource in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --lock <tripleo-service> <pcmk-resource> <pcmk-bundle> <target-state-local> <target-state-cluster>\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\nACTION=$1\ncase $ACTION in\n    --help) usage; exit 0;;\n    --lock) ;;\n    *) error \"Unknown action '$ACTION'\";;\nesac\n\nTRIPLEO_SERVICE=$2\nLOCK_NAME=${TRIPLEO_SERVICE}-restart-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to restart resource $TRIPLEO_SERVICE\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\nRESOURCE_NAME=$3\nBUNDLE_NAME=$4\nWAIT_TARGET_LOCAL=$5\nWAIT_TARGET_ANYWHERE=${6:-_}\n\nif pacemaker_supports_promoted; then\n    WAIT_TARGET_LOCAL=$(echo \"$5\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    WAIT_TARGET_ANYWHERE=$(echo \"${6:-_}\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    promoted_role=\"Promoted\"\nelse\n    promoted_role=\"Master\"\nfi\n\n# The lock TTL should accomodate for the resource start/promote timeout\nif [ \"$RESOURCE_NAME\" != \"$BUNDLE_NAME\" ]; then\n    if [ \"$WAIT_TARGET_LOCAL\" = \"$promoted_role\" ] || [ \"$WAIT_TARGET_ANYWHERE\" = \"$promoted_role\" ]; then\n        rsc_op=\"promote\"\n    else\n        rsc_op=\"start\"\n    fi\n    # <op id=\"galera-promote-interval-0s\" interval=\"0s\" name=\"promote\" on-fail=\"block\" timeout=\"300s\"/>\n    PCMK_TTL=$(cibadmin -Q | xmllint -xpath \"string(//primitive[@id='${RESOURCE_NAME}']/operations/op[@name='${rsc_op}']/@timeout)\" - | sed 's/s$//')\n    LOCK_TTL=$((PCMK_TTL + 30))\nelse\n    # The podman RA's default start timeout\n    LOCK_TTL=90\nfi\n\nlog \"Acquire a ${LOCK_TTL}s restart lock for service $TRIPLEO_SERVICE before restarting it\"\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nrc=1\nwhile [ $rc -ne 0 ]; do\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire $LOCK_NAME $LOCK_OWNER $LOCK_TTL\n    rc=$?\n    if [ $rc != 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not acquire lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Could not acquire lock, retrying\"\n            sleep 10\n        fi\n    fi\ndone\n\nlog \"Restart the service $TRIPLEO_SERVICE locally\"\n# Reuse the local restart script in t-h-t (driven by env var TRIPLEO_MINOR_UPDATE)\nTRIPLEO_MINOR_UPDATE=true /var/lib/container-config-scripts/pacemaker_restart_bundle.sh $TRIPLEO_SERVICE $RESOURCE_NAME $BUNDLE_NAME $WAIT_TARGET_LOCAL $WAIT_TARGET_ANYWHERE\n\n# If we reached this point, always try to release the lock\nlog \"Release the restart lock for service $TRIPLEO_SERVICE\"\n/var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $LOCK_NAME $LOCK_OWNER\nrc=$?\nif [ $rc -ne 0 ] && [ $rc -ne 1 ]; then\n    error \"Could not release held lock (rc: $rc)\"\nfi\n"}, "pacemaker_mutex_shutdown.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_shutdown.sh --acquire\n# pacemaker_mutex_shutdown.sh --release\n\nset -u\n\nusage() {\n    echo \"Shutdown a cluster node in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --acquire # prevent other node from shutting down until we hold the lock\"\n    echo \"   $0 --release # release the lock, other node can compete for the shutdown lock\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nshutdown_lock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc=1\n    local current_owner\n    local owner_stopped\n    local owner_rc\n\n    log \"Acquiring the shutdown lock\"\n    while [ $rc -ne 0 ]; do\n        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire-once $lockname $requester $ttl\n        rc=$?\n        if [ $rc -ne 0 ]; then\n            if [ $rc -eq 2 ]; then\n                error \"Could not acquire the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n            else\n                # The lock is held by another node.\n                current_owner=$(/var/lib/container-config-scripts/pacemaker_resource_lock.sh --owner $lockname)\n                owner_rc=$?\n                if [ $owner_rc -eq 2 ]; then\n                    error \"Could not get the shutdown lock owner due to unrecoverable error (rc: $owner_rc), bailing out\"\n                fi\n                if [ $owner_rc -eq 0 ]; then\n                    # If the owner is marked as offline, that means it has shutdown and\n                    # we can clean the lock preemptively and try to acquire it.\n                    owner_stopped=$(crm_mon -1X | xmllint --xpath 'count(//nodes/node[@name=\"'${current_owner}'\" and @online=\"false\" and @unclean=\"false\"])' -)\n                    if [ \"${owner_stopped}\" = \"1\" ]; then\n                        log \"Shutdown lock held by stopped node '${current_owner}', lock can be released\"\n                        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $current_owner\n                        continue\n                    fi\n                fi\n                log \"Shutdown lock held by another node (rc: $rc), retrying\"\n                sleep 10\n            fi\n        fi\n    done\n    log \"Shutdown lock acquired\"\n    return 0\n}\n\n\n# Release the lock if we still own it. Not owning it anymore is not fatal\nshutdown_lock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n\n    log \"Releasing the shutdown lock\"\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $requester\n    rc=$?\n    if [ $rc -ne 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not release the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Shutdown lock no longer held, nothing to do\"\n        fi\n    else\n        log \"Shutdown lock released\"\n    fi\n    return 0\n}\n\n\nACTION=$1\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nLOCK_NAME=tripleo-shutdown-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to aquire the shutdown lock\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\n# We start with a very high TTL, that long enough to accomodate a cluster stop.\n# As soon as the node will get offline, the other competing node will be entitled\n# to steal the lock, so they should never wait that long in practice.\nLOCK_TTL=600\n\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) shutdown_lock_acquire ${LOCK_NAME} ${LOCK_OWNER} ${LOCK_TTL};;\n    --release|-r) shutdown_lock_release ${LOCK_NAME} ${LOCK_OWNER};;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro"], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-34-awy7ykbh3hec-ContainersCommon-r2ez2tfay55q/e41fa4a1-a589-4769-8775-eb3ee0d0ea86"}


created_at: 2026-06-13 07:32:09.252825
data:
  {"tuple:('role_data', 'config_settings')": {"nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo"}, "tuple:('role_data', 'service_config_settings', 'mysql')": {"nova::db::mysql::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql::user": "nova", "nova::db::mysql::host": "%", "nova::db::mysql::dbname": "nova"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-34-awy7ykbh3hec-NovaDBClient-v2r7zncn32rn/baeaf60d-672c-42fd-ad14-ac11afd85ee3"}


created_at: 2026-06-13 07:32:09.319264
data:
  {"tuple:('role_data', 'deploy_steps_tasks')": [{"name": "Certificate generation", "when": ["step|int == 1", "enable_internal_tls"], "block": [{"name": "Create dirs for certificates and keys", "file": {"path": "{{ item }}", "state": "directory", "serole": "object_r", "setype": "cert_t", "seuser": "system_u"}, "with_items": ["/etc/pki/tls/certs/httpd", "/etc/pki/tls/private/httpd"]}, {"include_role": {"name": "linux-system-roles.certificate"}, "vars": {"certificate_requests": [{"name": "httpd-ctlplane", "dns": "{{ fqdn_ctlplane }}", "principal": "HTTP/{{ fqdn_ctlplane }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-ctlplane.crt /etc/pki/tls/certs/httpd/httpd-ctlplane.crt\ncp /etc/pki/tls/private/httpd-ctlplane.key /etc/pki/tls/private/httpd/httpd-ctlplane.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-storage", "dns": "{{ fqdn_storage }}", "principal": "HTTP/{{ fqdn_storage }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-storage.crt /etc/pki/tls/certs/httpd/httpd-storage.crt\ncp /etc/pki/tls/private/httpd-storage.key /etc/pki/tls/private/httpd/httpd-storage.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-storage_mgmt", "dns": "{{ fqdn_storage_mgmt }}", "principal": "HTTP/{{ fqdn_storage_mgmt }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-storage_mgmt.crt /etc/pki/tls/certs/httpd/httpd-storage_mgmt.crt\ncp /etc/pki/tls/private/httpd-storage_mgmt.key /etc/pki/tls/private/httpd/httpd-storage_mgmt.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-internal_api", "dns": "{{ fqdn_internal_api }}", "principal": "HTTP/{{ fqdn_internal_api }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-internal_api.crt /etc/pki/tls/certs/httpd/httpd-internal_api.crt\ncp /etc/pki/tls/private/httpd-internal_api.key /etc/pki/tls/private/httpd/httpd-internal_api.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-external", "dns": "{{ fqdn_external }}", "principal": "HTTP/{{ fqdn_external }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-external.crt /etc/pki/tls/certs/httpd/httpd-external.crt\ncp /etc/pki/tls/private/httpd-external.key /etc/pki/tls/private/httpd/httpd-external.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}]}}]}], "tuple:('role_data', 'metadata_settings')": [[{"service": "HTTP", "network": "ctlplane", "type": "node"}], [{"service": "HTTP", "network": "storage", "type": "node"}], [{"service": "HTTP", "network": "storage_mgmt", "type": "node"}], [{"service": "HTTP", "network": "internal_api", "type": "node"}], [{"service": "HTTP", "network": "external", "type": "node"}]], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-34-awy7ykbh3hec-ApacheServiceBase-iqcdskzetgtt/f72439b5-f248-4d1e-8670-992e15030c3c"}


created_at: 2026-06-13 07:32:09.349082
data:
  {"tuple:('role_data', 'config_settings')": {"nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo"}, "tuple:('role_data', 'service_config_settings', 'mysql')": {"nova::db::mysql_api::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql_api::user": "nova_api", "nova::db::mysql_api::host": "%", "nova::db::mysql_api::dbname": "nova_api"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-34-awy7ykbh3hec-NovaApiDBClient-76snyh5grzlh/239faa18-360c-4c08-bfe3-8b491e9fa9aa"}


created_at: 2026-06-13 07:32:09.403831
data:
  {"tuple:('role_data', 'step_config')": "include tripleo::profile::base::database::mysql::client", "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-34-awy7ykbh3hec-MySQLClient-ihwkhvo7ryfd/4a4014a9-60c1-4f46-9bce-c4b6a3095784"}


created_at: 2026-06-13 07:32:09.504233
data:
  {"tuple:('role_data', 'config_settings', 'nova::logging::debug')": true, "tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": ""}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-34-awy7ykbh3hec-NovaBase-hedb6w63iv5v/ed56a99f-ae2c-4724-9294-aefe8cac2df1"}


created_at: 2026-06-13 07:32:09.511996
data:
  {"value": {"ContainerNovaApiImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "ContainerNovaConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:09.609437
data:
  {"volumes": ["/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z"], "tuple:('docker_config', 'step_2')": {"nova_api_init_logs": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "config_settings": null, "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "setype": "{{ item.setype }}", "state": "directory", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}, {"path": "/var/log/containers/httpd/nova-api", "setype": "container_file_t", "mode": "0750"}]}], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-34-awy7ykbh3hec-NovaApiLogging-palqjsuarfnt/4727d8c1-8fba-4a44-a1d0-f81917442a8e"}


created_at: 2026-06-13 07:32:09.626298
data:
  {"tuple:('role_data', 'step_config')": null, "tuple:('role_data', 'container_config_scripts')": {"container_puppet_apply.sh": {"mode": "0700", "content": "#!/bin/bash\nset -eux\nSTEP=$1\nTAGS=$2\nCONFIG=$3\nEXTRA_ARGS=${4:-''}\nif [ -d /tmp/puppet-etc ]; then\n  # ignore copy failures as these may be the same file depending on docker mounts\n  cp -a /tmp/puppet-etc/* /etc/puppet || true\nfi\necho \"{\\\"step\\\": ${STEP}}\" > /etc/puppet/hieradata/docker_puppet.json\n# $::deployment_type in puppet-tripleo\nexport FACTER_deployment_type=containers\nset +e\npuppet apply $EXTRA_ARGS \\\n    --verbose \\\n    --detailed-exitcodes \\\n    --summarize \\\n    --color=false \\\n    --modulepath /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules \\\n    --tags $TAGS \\\n    -e \"noop_resource('package'); ${CONFIG}\"\nrc=$?\nset -e\nset +ux\nif [ $rc -eq 2 -o $rc -eq 0 ]; then\n    exit 0\nfi\nexit $rc\n"}, "pyshim.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n#\n# Usage: pyshim.sh <script and/or arguments>\n#\n# Unfortunately THT doesn't know which version of python might be in a\n# container so we need this script to be able to try python3 or python2\n# depending on availability.  Since this is a temporary shim until we've\n# fully cut over to python3, we check for the existence of python3 first\n# before falling back to python2. This will help in the transition from\n# python2 based containers to python3.\n\nshow_usage() {\n    echo \"Usage: pyshim.sh <script and/or arguments>\"\n}\n\nif [ $# -lt 1 ]\nthen\n    show_usage\n    exit 1\nfi\n\nset -x\nif command -v python3 >/dev/null; then\n    python3 \"$@\"\nelif command -v python2 >/dev/null; then\n    python2 \"$@\"\nelif command -v python >/dev/null; then\n    python \"$@\"\nelse\n    echo \"ERROR: python is not available!\"\n    exit 1\nfi\n"}, "pacemaker_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nset -u\n\n# ./pacemaker_restart_bundle.sh mysql galera galera-bundle Master _\n# ./pacemaker_restart_bundle.sh redis redis redis-bundle Slave Master\n# ./pacemaker_restart_bundle.sh ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\nRESTART_SCRIPTS_DIR=$(dirname $0)\nTRIPLEO_SERVICE=$1\nRESOURCE_NAME=$2\nBUNDLE_NAME=$3\nWAIT_TARGET_LOCAL=$4\nWAIT_TARGET_ANYWHERE=${5:-_}\nTRIPLEO_MINOR_UPDATE=\"${TRIPLEO_MINOR_UPDATE:-false}\"\nTRIPLEO_HA_WRAPPER_RESOURCE_EXISTS=\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS:-false}\"\n\nbundle_can_be_restarted() {\n    local bundle=$1\n    # As long as the resource bundle is managed by pacemaker and is\n    # not meant to stay stopped, no matter the state of any inner\n    # pcmk_remote or ocf resource, we should restart it to give it a\n    # chance to read the new config.\n    [ \"$(crm_resource --meta -r $1 -g is-managed 2>/dev/null)\" != \"false\" ] && \\\n    [ \"$(crm_resource --meta -r $1 -g target-role 2>/dev/null)\" != \"Stopped\" ]\n}\n\nlog() {\n    local msg=$1\n    logger -t pcmkrestart \"$1\"\n}\n\nHOSTNAME=$(/bin/hostname -s)\nif [ x\"${TRIPLEO_MINOR_UPDATE,,}\" != x\"true\" ]; then\n    if [ x\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS,,}\" = x\"false\" ]; then\n        # Do not restart during initial deployment, as the resource\n        # has just been created.\n        SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n        if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n            log \"Initial deployment, skipping the restart of ${BUNDLE_NAME}\"\n\tfi\n        exit 0\n    else\n        # During a stack update, this script is called in parallel on\n        # every node the resource runs on, after the service's configs\n        # have been updated on all nodes. So we need to run pcs only\n        # once (e.g. on the service's boostrap node).\n        if bundle_can_be_restarted ${BUNDLE_NAME}; then\n            SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n            if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n                replicas_running=$(crm_resource -Q -r $BUNDLE_NAME --locate 2>&1 | wc -l)\n                if [ \"$replicas_running\" != \"0\" ]; then\n                    log \"Restarting ${BUNDLE_NAME} globally. Stopping:\"\n                    /sbin/pcs resource disable --wait=600 $BUNDLE_NAME\n                    log \"Restarting ${BUNDLE_NAME} globally. Starting:\"\n                    /sbin/pcs resource enable --wait=600 $BUNDLE_NAME\n                else\n                    log \"${BUNDLE_NAME} is not running anywhere,\" \\\n                         \"cleaning up to restart it globally if necessary\"\n                    /sbin/pcs resource cleanup $BUNDLE_NAME\n                fi\n            else\n                log \"Skipping global restart of ${BUNDLE_NAME} on ${HOSTNAME} it will be restarted by node ${SERVICE_NODEID}\"\n            fi\n\n        else\n            log \"No global restart needed for ${BUNDLE_NAME}.\"\n        fi\n    fi\nelse\n    # During a minor update workflow however, a host gets fully\n    # updated before updating the next one. So unlike stack\n    # update, at the time this script is called, the service's\n    # configs aren't updated on all nodes yet. So only restart the\n    # resource locally, where it's guaranteed that the config is\n    # up to date.\n    HOST=$(facter hostname)\n\n    if bundle_can_be_restarted ${BUNDLE_NAME}; then\n\t# if the resource is running locally, restart it\n\tif crm_resource -r $BUNDLE_NAME --locate 2>&1 | grep -w -q \"${HOST}\"; then\n            log \"Restarting ${BUNDLE_NAME} locally on '${HOST}'\"\n            /sbin/pcs resource restart $BUNDLE_NAME \"${HOST}\"\n\n\telse\n\t    # At this point, if no resource is running locally, it's\n\t    # either because a) it has failed previously, or b) because\n\t    # it's an A/P resource running elsewhere.\n\t    # By cleaning up resource, we ensure that a) it will try to\n\t    # restart, or b) it won't do anything if the resource is\n\t    # already running elsewhere.\n            log \"${BUNDLE_NAME} is currently not running on '${HOST}',\" \\\n                 \"cleaning up its state to restart it if necessary\"\n            /sbin/pcs resource cleanup $BUNDLE_NAME node=\"${HOST}\"\n\tfi\n\n\t# Wait until the resource is in the expected target state\n\t$RESTART_SCRIPTS_DIR/pacemaker_wait_bundle.sh \\\n            $RESOURCE_NAME $BUNDLE_NAME \\\n            \"$WAIT_TARGET_LOCAL\" \"$WAIT_TARGET_ANYWHERE\" \\\n\t    \"${HOST}\" 600\n    else\n        log \"No restart needed for ${BUNDLE_NAME}.\"\n    fi\nfi\n"}, "pacemaker_wait_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# ----\n# Wait for an OCF resource or a bundle to be restarted\n# ----\n# e.g.:\n# M/S OCF:      $0 galera galera-bundle Master\n# clone OCF:    $0 rabbitmq rabbitmq-bundle Started\n# A/P M/S OCF:  $0 redis redis-bundle Slave Master\n# A/P bundle:   $0 openstack-cinder-volume openstack-cinder-volume _ Started\n# clone bundle: $0 haproxy-bundle haproxy-bundle Started\n\n# design note 1:\n#  - this script is called during a minor update; it is called\n#    once per node that hosts a service replica.\n#  - the purpose of this script is to ensure that restarting the\n#    service replica locally won't disrupt the service availability\n#    for the end user. To reach that goal, the script waits until the\n#    service is restarted locally or globallu and reaches a given\n#    target state (i.e. Started, Slave or Master).\n# design note 2:\n#   - we don't want to track restart error: our only job is to ensure\n#     service restart synchronization, not service health.\n#   - In particular, we don't want to error out in case the resource\n#     cannot be restarted locally, because that would make the minor\n#     update fail, even if potentially other replicas still provide\n#     the service.\n# design note 3:\n#   - we can bail out early if we determine that the resource can't\n#     be restarted automatically by pacemaker (e.g. its \"blocked\",\n#     unmanaged or disabled).\n\nlog() {\n    local msg=$1\n    echo \"$(date -u): $1\"\n}\n\nusage() {\n    echo 2>&1 \"Usage: $0 NAME BUNDLE_NAME ROLE_LOCAL [ROLE_ANYWHERE] [HOST] [TIMEOUT]\"\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\n#\n# Utility functions to detect stuck resources\n#\n\nbundle_failures_locally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local replica_name\n    for i in $(seq 0 $last); do\n\treplica_name=${BUNDLE_NAME}-${engine}-${i}\n\tcrm_failcount -q -G -r $replica_name -N $HOST\n    done\n}\n\nbundle_failures_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    for i in $(seq 0 $last); do\n\tcrm_failcount -q -G -r ${BUNDLE_NAME}-${engine}-${i}\n    done\n}\n\nbundle_running_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    # return the number of running bundles replica, i.e. the number of\n    # docker/podman resource replicas currently running in the cluster\n    crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:heartbeat:${engine}']/node)\" -\n}\n\nocf_failures_globally() {\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local bundle_node\n    for i in $(seq 0 $last); do\n\tbundle_node=${BUNDLE_NAME}-${i}\n\tcrm_failcount -q -G -r $NAME -N $bundle_node\n    done\n}\n\ndid_resource_failed_locally() {\n    local failures\n    local running\n    local remotehost\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# if we're dealing with an ocf resource, it is running on a\n\t# pacemaker_remote rather that on the real host, and the\n\t# failcounts are thus associated to the pcmk remote. Replace\n\t# the host's name with the pcmk remote's name.\n        remotehost=$(crm_mon --as-xml | xmllint --xpath \"string(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource/node[@name='${HOST}']/../../resource[@resource_agent='${OCF}:pacemaker:remote']/@id)\" -)\n\tif [ -n \"${remotehost}\" ]; then\n\t    crm_failcount -q -G -r $NAME -N $remotehost | grep -q -w INFINITY\n\t    return $?\n\tfi\n\t# If no pcmk remote is currently running, the failcount from\n\t# the ocf resource is useless, compute the failcount from the\n\t# bundle case instead (computed below).\n    fi\n\n    # for bundles, pacemaker can run any bundle replica locally\n    # (e.g. galera-bundle-docker-{0,1,2}), and a failure happens when\n    # there are no more replica to try.\n    # That is, when _at least_ one replica failed locally, and all the\n    # others either failed or are currently running elsewhere.\n    failures=$(bundle_failures_locally $HOST | grep -c -w INFINITY)\n    running=$(bundle_running_globally)\n    test $failures -gt 0 && \\\n    test $(( $failures + $running )) -ge $BUNDLE_REPLICAS\n}\n\ndid_resource_failed_globally() {\n    local remotecount\n    local failures\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# we check the state of an ocf resource only if the\n\t# pcmkremotes are started\n        remotecount=$(crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node)\" -)\n\tif [ \"${remotecount}\" = \"0\" ]; then\n\t    # no pcmkremote is running, so check the bundle state\n\t    # instead of checking the ocf resource\n\t    # bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\t    failures=$(bundle_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\telse\n\t    # ocf resource failed if it failed to start on\n\t    # all $BUNDLE_REPLICAS bundle nodes\n\t    failures=$(ocf_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\tfi\n    else\n\t# bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\tfailures=$(bundle_failures_globally | grep -c -w INFINITY)\n\ttest $failures -eq $BUNDLE_REPLICAS\n    fi\n}\n\n\n# Input validation\n#\n\nNAME=$1\nif [ -z \"${NAME}\" ]; then\n    echo 2>&1 \"Error: argument NAME must not be empty\"\n    exit 1\nfi\n\nBUNDLE_NAME=$2\nif [ -z \"${BUNDLE_NAME}\" ]; then\n    echo 2>&1 \"Error: argument BUNDLE_NAME must not be empty\"\n    exit 1\nfi\n\nROLE_LOCAL=$3\nif [ \"${ROLE_LOCAL}\" = \"_\" ]; then\n    ROLE_LOCAL=\"\"\nfi\n\nROLE_ANYWHERE=$4\nif [ \"${ROLE_ANYWHERE}\" = \"_\" ]; then\n    ROLE_ANYWHERE=\"\"\nfi\n\nif [ -z \"${ROLE_LOCAL}\" ]; then\n    if [ -z \"${ROLE_ANYWHERE}\" ]; then\n        echo 2>&1 \"Error: either ROLE_LOCAL or ROLE_ANYWHERE must be non empty\"\n        exit 1\n    fi\nelse\n    if !(echo \"${ROLE_LOCAL}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n        echo 2>&1 \"Error: argument ROLE_LOCAL must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n        exit 1\n    fi\nfi\n\nif [ -n \"${ROLE_ANYWHERE}\" ] && !(echo \"${ROLE_ANYWHERE}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n    echo 2>&1 \"Error: argument ROLE_ANYWHERE must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n    exit 1\nfi\n\n# Ensure compatibility with pacemaker 2.1\nif pacemaker_supports_promoted; then\n    ROLE_LOCAL=$(echo \"$ROLE_LOCAL\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    ROLE_ANYWHERE=$(echo \"$ROLE_ANYWHERE\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    OCF=\"ocf\"\nelse\n    OCF=\"ocf:\"\nfi\n\nHOST=${5:-$(facter hostname)}\nTIMEOUT=${6:-600}\n\n\n# Configure the search\n# ----\n# Note: we can't use crm_resource in all searches because we can't\n# easily extract the host the OCF resources run on (crm_resource\n# returns the pcmk-remote nodes rather than the hosts)\n# So instead, we implement various searches with XPath directly.\n\nif [ \"${BUNDLE_NAME}\" != \"${NAME}\" ]; then\n# ocf resource\nlocal_resource_xpath=\"//bundle/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node[@name='${HOST}']/../../resource[@id='${NAME}']\"\nany_resource_xpath=\"//bundle//resource[@id='${NAME}']\"\nreplicas_xpath=\"//bundle/primitive[@id='${BUNDLE_NAME}']/../*[boolean(@image) and boolean(@replicas)]\"\nelse\n# bundle resource\nlocal_resource_xpath=\"//bundle[@id='${NAME}']/replica/resource/node[@name='${HOST}']/../../resource\"\nany_resource_xpath=\"//bundle[@id='${NAME}']//resource\"\nreplicas_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nfi\n\nbundle_def_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nBUNDLE_CONTAINER_ENGINE=$(cibadmin -Q | xmllint --xpath \"name(${bundle_def_xpath})\" -)\nBUNDLE_REPLICAS=$(cibadmin -Q | xmllint --xpath \"string(${bundle_def_xpath}/@replicas)\" -)\n\n\n# The wait algorithm follows a two-stage approach\n#  1. Depending on how the script is called, we first check whether\n#     the resource is restarted locally. An A/P resource may be\n#     restarted elsewhere in the cluster.\n#  2. If needed, check whether the A/P resource has restarted\n#     elsewhere. For A/P M/S resources, in case the resource is\n#     restarted as Slave locally, ensure a Master is available.\n\nsuccess=1\nbailout=1\ntimeout=$TIMEOUT\nrole=\"\"\n\n# Stage 1: local check\nif [ -n \"$ROLE_LOCAL\" ]; then\n    log \"Waiting until ${NAME} has restarted on ${HOST} and is in state ${ROLE_LOCAL}\"\n    log \"Will probe resource state with the following XPath pattern: ${local_resource_xpath}\"\n\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resource=$(crm_mon -r --as-xml | xmllint --xpath \"${local_resource_xpath}\" - 2>/dev/null)\n        role=$(echo \"${resource}\" | sed -ne 's/.*\\Wrole=\"\\([^\"]*\\)\".*/\\1/p')\n\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resource}\" | grep -q -w \"\\Wblocked=\\\"true\\\"\"; then\n            log \"${NAME} is blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_locally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif [ \"$role\" = \"$ROLE_LOCAL\" ]; then\n            success=0\n            continue\n        elif [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" = \"$ROLE_ANYWHERE\" ]; then\n            # A/P: we are restarted in the expected state\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_LOCAL} on ${HOST}\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\n# Stage 2: global check\nif [ $timeout -gt 0 ] && [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" != \"$ROLE_ANYWHERE\" ]; then\n    log \"Wait until ${NAME} is restarted anywhere in the cluster in state ${ROLE_ANYWHERE}\"\n    log \"Will probe resource state with the following XPath pattern: ${any_resource_xpath}\"\n\n    success=1\n    bailout=1\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resources=$(crm_mon -r --as-xml | xmllint --xpath \"${any_resource_xpath}\" - 2>/dev/null)\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif ! (echo \"${resources}\" | grep -q -w \"\\Wblocked=\\\"false\\\"\"); then\n            log \"${NAME} blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_globally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resources}\" | grep -q -w \"\\Wrole=\\\"${ROLE_ANYWHERE}\\\"\"; then\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_ANYWHERE} anywhere in the cluster\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\nif [ $timeout -le 0 ]; then\n    log \"Timeout reached after ${TIMEOUT}s while waiting for ${NAME} to be restarted\"\nelif [ $bailout -le 0 ]; then\n    log \"Restart monitoring for ${NAME} cancelled\"\nfi\n\nif [ $success -eq 0 ]; then\n    log \"${NAME} successfully restarted\"\nelse\n    log \"${NAME} was not restarted properly\"\nfi\n\n# Don't block minor update or stack update if the wait was unsuccessful\nexit 0\n"}, "wait-port-and-run.sh": {"mode": "0755", "content": "#!/usr/bin/bash\nset -eu\n\nHOST=$1\nPORT=$2\n\necho \"$(date -u): Checking whether we can bind to ${HOST}:${PORT}\"\nwhile (ss -Htnl src \"${HOST}\" \"sport = :${PORT}\" | grep -wq \"${PORT}\"); do\n    echo \"$(date -u): ${HOST}:${PORT} still in use, waiting...\";\n    sleep 10;\ndone\n\nshift 2\nCOMMAND=\"$*\"\nif [ -z \"${COMMAND}\" ]; then\n    COMMAND=\"true\"\nfi\nexec $COMMAND\n"}, "pacemaker_resource_lock.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nMAX_RETRIES=10\nCIB_ENOTFOUND=105\n\nusage() {\n   echo \"Set a global property in the cluster with a validity timestamp.\"\n   echo \"Usage:\"\n   echo \"   $0 --acquire <lock_name> <lock_owner> <lock_ttl_in_seconds>\"\n   echo \"   $0 --release <lock_name> <lock_owner>\"\n   echo\n}\n\nlog() {\n    echo \"$(date -u): $1\" 1>&2\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\nlock_create() {\n    local name=$1\n    local data=$2\n    # cibadmin won't overwrite a key if someone else succeeded to create it concurrently\n    cibadmin --sync-call --scope crm_config --create --xml-text \"<cluster_property_set id='${name}'><nvpair id='${name}-pair' name='${name}' value='${data}'/></cluster_property_set>\" &>/dev/null\n    return $?\n}\n\nlock_update() {\n    local name=$1\n    local expected_data=$2\n    local new_data=$3\n    # we only update the lock we expect to see, so we can't update someone else's lock\n    cibadmin --sync-call --scope crm_config --modify --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" --xml-text \"<nvpair id='${name}-pair' name='${name}' value='${new_data}'/>\" &>/dev/null\n    return $?\n}\n\nlock_delete() {\n    local name=$1\n    local expected_data=$2\n    # we only delete the lock we expect to see, so we can't delete someone else's lock\n    cibadmin --sync-call --scope crm_config --delete --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" &>/dev/null\n    return $?\n}\n\nlock_get() {\n    local lockname=$1\n    local res\n    local rc\n    res=$(cibadmin --query --scope crm_config --xpath \"//cluster_property_set/nvpair[@name='$lockname']\" 2>/dev/null)\n    rc=$?\n    if [ $rc -eq 0 ]; then\n        echo \"$res\" | sed -n 's/.*value=\"\\([^\"]*\\)\".*/\\1/p'\n    fi\n    return $rc\n}\n\nlock_owner() {\n    local lock=$1\n    echo \"$lock\" | cut -d':' -f1\n}\n\nlock_has_expired() {\n    local lock=$1\n    local expiry=$(echo \"$lock\" | cut -d':' -f2)\n    local now=$(date +%s)\n    test $now -ge $expiry\n}\n\n\n# Perform a lock action and restart if the CIB has been modified before\n# committing the lock action\ntry_action() {\n    local fun=$1\n    local lock=$2\n    local requester=$3\n    local args=${4:-}\n    local tries=$MAX_RETRIES\n    local rc=1\n    if [ \"$fun\" = \"lock_acquire\" ] || [ \"$fun\" = \"lock_release\" ]; then\n        log \"Try running $fun\"\n    else\n        return 2\n    fi\n    while [ $rc -ne 0 ]; do\n        $fun $lock $requester $args\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Operation $1 succeeded\"\n            return 0\n        elif [ $rc -eq 3 ]; then\n            # rc == 3 -> CIB changed before push\n            if [ $tries -eq 0 ]; then\n                log \"Failed to commit after $MAX_RETRIES retries. Bailing out.\"\n                return 2\n            else\n                log \"Failed to commit. Retrying operation.\"\n                tries=$(($tries - 1))\n            fi\n        elif [ $rc -eq 2 ]; then\n            # rc == 2 -> unrecoverable cib error (e.g. pacemaker down)\n            log \"Unexpected failure. Bailing out\"\n            return $rc\n        else\n            # rc == 1 -> lock error (not owner, lock doesn't exists)\n            return $rc\n        fi\n    done\n}\n\n# The lock mechanism uses cibadmin's atomic creation so cluster-wide\n# state coherency is guaranteed by pacemaker\nlock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc\n    local lock\n    local expiry\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -n \"$lock\" ]; then\n        lock_has_expired $lock\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Lock has expired, now available for being held\"\n        else\n            # lock is still held. check whether we're the owner\n            owner=$(lock_owner $lock)\n            if [ \"$owner\" = \"$requester\" ];then\n                log \"Requester already owns the lock, acquiring attempt will just reconfigure the TTL\"\n            else\n                log \"Lock is held by someone else ($owner)\"\n                return 1\n            fi\n        fi\n    else\n        log \"Lock is not held yet\"\n    fi\n\n    # prepare the lock info\n    expiry=$(($(date +%s) + $ttl))\n\n    if [ -n \"$lock\" ]; then\n        log \"Attempting to update the lock\"\n        lock_update $lockname \"$lock\" \"$requester:$expiry\"\n        rc=$?\n    else\n        log \"Attempting to acquire the lock\"\n        lock_create $lockname \"$requester:$expiry\"\n        rc=$?\n    fi\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' acquired by '$requester', valid until $(date -d @$expiry)\"\n        return 0\n    else\n        log \"CIB changed, lock cannot be acquired\"\n        return 3\n    fi\n}\n\n\n# The lock mechanism uses the CIB's num_updates tag to implement\n# a conditional store. Cluster-wide locking is guaranteed by pacemaker\nlock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n    local lock\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        log \"Lock doesn't exist. Nothing to release\"\n        return 0\n    else\n        log \"Lock exists, check whether we're the owner\"\n        owner=$(lock_owner $lock)\n        if [ \"$owner\" != \"$requester\" ];then\n            log \"Lock is held by someone else ($owner), will not unlock\"\n            return 1\n        fi\n    fi\n\n    lock_delete $lockname \"$lock\"\n    rc=$?\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' released by '$requester'\"\n        return 0\n    else\n        log \"CIB deletion error, lock cannot be released\"\n        return 3\n    fi\n}\n\n\n# Retrieve the owner of a lock from the CIB\n# this is a read-only operation, so no need to log debug info\nlock_get_owner() {\n    local lockname=$1\n    local rc\n    local lock\n    local owner\n\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        return 1\n    else\n        lock_owner $lock\n        return 0\n    fi\n}\n\n\nACTION=$1\nLOCKNAME=$2\nREQUESTER=$3\nTTL=${4:-60}\n\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nif [ $ACTION != \"--help\" ]; then\n    if [ -z \"$LOCKNAME\" ]; then\n        error \"You must specific a lock name\"\n    fi\n    if [ $ACTION != \"--owner\" ] && [ $ACTION != \"-o\" ]; then\n        if [ -z \"$REQUESTER\" ]; then\n            error \"You must specific a lock requester\"\n        fi\n    fi\nfi\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) try_action lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --release|-r) try_action lock_release $LOCKNAME $REQUESTER;;\n    --acquire-once|-A) lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --owner|-o) lock_get_owner $LOCKNAME;;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "pacemaker_mutex_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_restart_bundle.sh --lock mysql galera galera-bundle Master _\n# pacemaker_mutex_restart_bundle.sh --lock ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\n\nset -u\n\nusage() {\n    echo \"Restart a clustered resource in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --lock <tripleo-service> <pcmk-resource> <pcmk-bundle> <target-state-local> <target-state-cluster>\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\nACTION=$1\ncase $ACTION in\n    --help) usage; exit 0;;\n    --lock) ;;\n    *) error \"Unknown action '$ACTION'\";;\nesac\n\nTRIPLEO_SERVICE=$2\nLOCK_NAME=${TRIPLEO_SERVICE}-restart-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to restart resource $TRIPLEO_SERVICE\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\nRESOURCE_NAME=$3\nBUNDLE_NAME=$4\nWAIT_TARGET_LOCAL=$5\nWAIT_TARGET_ANYWHERE=${6:-_}\n\nif pacemaker_supports_promoted; then\n    WAIT_TARGET_LOCAL=$(echo \"$5\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    WAIT_TARGET_ANYWHERE=$(echo \"${6:-_}\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    promoted_role=\"Promoted\"\nelse\n    promoted_role=\"Master\"\nfi\n\n# The lock TTL should accomodate for the resource start/promote timeout\nif [ \"$RESOURCE_NAME\" != \"$BUNDLE_NAME\" ]; then\n    if [ \"$WAIT_TARGET_LOCAL\" = \"$promoted_role\" ] || [ \"$WAIT_TARGET_ANYWHERE\" = \"$promoted_role\" ]; then\n        rsc_op=\"promote\"\n    else\n        rsc_op=\"start\"\n    fi\n    # <op id=\"galera-promote-interval-0s\" interval=\"0s\" name=\"promote\" on-fail=\"block\" timeout=\"300s\"/>\n    PCMK_TTL=$(cibadmin -Q | xmllint -xpath \"string(//primitive[@id='${RESOURCE_NAME}']/operations/op[@name='${rsc_op}']/@timeout)\" - | sed 's/s$//')\n    LOCK_TTL=$((PCMK_TTL + 30))\nelse\n    # The podman RA's default start timeout\n    LOCK_TTL=90\nfi\n\nlog \"Acquire a ${LOCK_TTL}s restart lock for service $TRIPLEO_SERVICE before restarting it\"\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nrc=1\nwhile [ $rc -ne 0 ]; do\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire $LOCK_NAME $LOCK_OWNER $LOCK_TTL\n    rc=$?\n    if [ $rc != 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not acquire lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Could not acquire lock, retrying\"\n            sleep 10\n        fi\n    fi\ndone\n\nlog \"Restart the service $TRIPLEO_SERVICE locally\"\n# Reuse the local restart script in t-h-t (driven by env var TRIPLEO_MINOR_UPDATE)\nTRIPLEO_MINOR_UPDATE=true /var/lib/container-config-scripts/pacemaker_restart_bundle.sh $TRIPLEO_SERVICE $RESOURCE_NAME $BUNDLE_NAME $WAIT_TARGET_LOCAL $WAIT_TARGET_ANYWHERE\n\n# If we reached this point, always try to release the lock\nlog \"Release the restart lock for service $TRIPLEO_SERVICE\"\n/var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $LOCK_NAME $LOCK_OWNER\nrc=$?\nif [ $rc -ne 0 ] && [ $rc -ne 1 ]; then\n    error \"Could not release held lock (rc: $rc)\"\nfi\n"}, "pacemaker_mutex_shutdown.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_shutdown.sh --acquire\n# pacemaker_mutex_shutdown.sh --release\n\nset -u\n\nusage() {\n    echo \"Shutdown a cluster node in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --acquire # prevent other node from shutting down until we hold the lock\"\n    echo \"   $0 --release # release the lock, other node can compete for the shutdown lock\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nshutdown_lock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc=1\n    local current_owner\n    local owner_stopped\n    local owner_rc\n\n    log \"Acquiring the shutdown lock\"\n    while [ $rc -ne 0 ]; do\n        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire-once $lockname $requester $ttl\n        rc=$?\n        if [ $rc -ne 0 ]; then\n            if [ $rc -eq 2 ]; then\n                error \"Could not acquire the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n            else\n                # The lock is held by another node.\n                current_owner=$(/var/lib/container-config-scripts/pacemaker_resource_lock.sh --owner $lockname)\n                owner_rc=$?\n                if [ $owner_rc -eq 2 ]; then\n                    error \"Could not get the shutdown lock owner due to unrecoverable error (rc: $owner_rc), bailing out\"\n                fi\n                if [ $owner_rc -eq 0 ]; then\n                    # If the owner is marked as offline, that means it has shutdown and\n                    # we can clean the lock preemptively and try to acquire it.\n                    owner_stopped=$(crm_mon -1X | xmllint --xpath 'count(//nodes/node[@name=\"'${current_owner}'\" and @online=\"false\" and @unclean=\"false\"])' -)\n                    if [ \"${owner_stopped}\" = \"1\" ]; then\n                        log \"Shutdown lock held by stopped node '${current_owner}', lock can be released\"\n                        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $current_owner\n                        continue\n                    fi\n                fi\n                log \"Shutdown lock held by another node (rc: $rc), retrying\"\n                sleep 10\n            fi\n        fi\n    done\n    log \"Shutdown lock acquired\"\n    return 0\n}\n\n\n# Release the lock if we still own it. Not owning it anymore is not fatal\nshutdown_lock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n\n    log \"Releasing the shutdown lock\"\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $requester\n    rc=$?\n    if [ $rc -ne 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not release the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Shutdown lock no longer held, nothing to do\"\n        fi\n    else\n        log \"Shutdown lock released\"\n    fi\n    return 0\n}\n\n\nACTION=$1\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nLOCK_NAME=tripleo-shutdown-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to aquire the shutdown lock\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\n# We start with a very high TTL, that long enough to accomodate a cluster stop.\n# As soon as the node will get offline, the other competing node will be entitled\n# to steal the lock, so they should never wait that long in practice.\nLOCK_TTL=600\n\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) shutdown_lock_acquire ${LOCK_NAME} ${LOCK_OWNER} ${LOCK_TTL};;\n    --release|-r) shutdown_lock_release ${LOCK_NAME} ${LOCK_OWNER};;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "nova_wait_for_api_service.py": {"mode": "0755", "content": "#!/usr/bin/python3\n#\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\nimport argparse\nimport logging\nimport os\nimport six\nimport sys\nimport time\n\n\nfrom keystoneauth1 import loading\nfrom keystoneauth1 import session\n\nfrom novaclient import client\nfrom novaclient.exceptions import ClientException\n\n# In python3 SafeConfigParser was renamed to ConfigParser and the default\n# for duplicate options default to true. In case of nova it is valid to\n# have duplicate option lines, e.g. passthrough_whitelist which leads to\n# issues reading the nova.conf\n# https://bugs.launchpad.net/tripleo/+bug/1827775\nif six.PY3:\n    from six.moves.configparser import ConfigParser\n    config = ConfigParser(strict=False)\nelse:\n    from six.moves.configparser import SafeConfigParser\n    config = SafeConfigParser()\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_wait_for_api_service')\n\niterations = 60\ntimeout = 10\nnova_cfg = '/etc/nova/nova.conf'\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(usage='%(prog)s [options]')\n    parser.add_argument('-k', '--insecure',\n                        action=\"store_false\",\n                        dest='insecure',\n                        default=True,\n                        help='Allow insecure connection when using SSL')\n\n    args = parser.parse_args()\n    LOG.debug('Running with parameter insecure = %s',\n              args.insecure)\n\n    if os.path.isfile(nova_cfg):\n        try:\n            config.read(nova_cfg)\n        except Exception:\n            LOG.exception('Error while reading nova.conf:')\n    else:\n        LOG.error('Nova configuration file %s does not exist', nova_cfg)\n        sys.exit(1)\n\n    loader = loading.get_plugin_loader('password')\n    auth = loader.load_from_options(\n        auth_url=config.get('neutron',\n                            'auth_url'),\n        username=config.get('neutron',\n                            'username'),\n        password=config.get('neutron',\n                            'password'),\n        project_name=config.get('neutron',\n                                'project_name'),\n        project_domain_name=config.get('neutron',\n                                       'project_domain_name'),\n        user_domain_name=config.get('neutron',\n                                    'user_domain_name'))\n    sess = session.Session(auth=auth, verify=args.insecure)\n\n    # Wait until this host is listed in the service list\n    for i in range(iterations):\n        try:\n            nova = client.Client('2.11', session=sess, endpoint_type='internal')\n            nova.versions.list()\n            LOG.info('Nova-api service active')\n            sys.exit(0)\n        except ClientException:\n            LOG.info('Waiting for nova-api service')\n        except Exception:\n            LOG.exception(\n                'Error while waiting for nova-api service')\n        time.sleep(timeout)\nsys.exit(1)\n\n# vim: set et ts=4 sw=4 :\n"}, "nova_api_ensure_default_cells.py": {"mode": "0755", "content": "#!/usr/bin/python3\n#\n# Copyright 2022 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\nfrom configparser import ConfigParser\nimport logging\nimport os\nimport subprocess\nimport sys\nfrom urllib import parse as urlparse\n\nconfig = ConfigParser(strict=False)\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_api_ensure_default_cells')\n\nNOVA_CFG = '/etc/nova/nova.conf'\nCELL0_ID = '00000000-0000-0000-0000-000000000000'\nDEFAULT_CELL_NAME = 'default'\n\n\ndef template_netloc_credentials(netloc, index=None):\n    if '@' in netloc:\n        userpass, hostport = netloc.split('@', 1)\n        has_pass = ':' in userpass\n        if index is None:\n            cred_template = '{username}'\n            if has_pass:\n                cred_template += ':{password}'\n        else:\n            cred_template = '{{username{index}}}'.format(index=index)\n            if has_pass:\n                cred_template += ':{{password{index}}}'.format(index=index)\n        return '@'.join((cred_template, hostport))\n    else:\n        return netloc\n\n\ndef template_url(url):\n    parsed = urlparse.urlparse(url)\n    if ',' in parsed.netloc:\n        orig_netlocs = parsed.netloc.split(',')\n        templ_netlocs = []\n        index = 0\n        for netloc in orig_netlocs:\n            index += 1\n            templ_netlocs.append(template_netloc_credentials(netloc, index))\n        new_netloc = ','.join(templ_netlocs)\n    else:\n        new_netloc = template_netloc_credentials(parsed.netloc)\n    return parsed._replace(netloc=new_netloc).geturl()\n\n\ndef parse_list_cells(list_cells_output):\n    list_cells_lines = list_cells_output.split('\\n')\n    if len(list_cells_lines) < 5:\n        raise ValueError('Invalid nova-manage cell_v2 list_cells output')\n\n    data_rows = list_cells_lines[3:-2]\n    by_name = {}\n    by_uuid = {}\n\n    for row in data_rows:\n        parts = row.split('|')\n        entry = {\n            'name': parts[1].strip(),\n            'uuid': parts[2].strip(),\n            'transport_url': parts[3].strip(),\n            'database_connection': parts[4].strip(),\n        }\n        by_name[entry['name']] = entry\n        by_uuid[entry['uuid']] = entry\n\n    return by_name, by_uuid\n\n\ndef create_or_update_default_cells(cell0_db, default_db, default_transport_url):\n    list_cells_cmd = ['/usr/bin/nova-manage', 'cell_v2', 'list_cells', '--verbose']\n    list_cells_output = subprocess.check_output(list_cells_cmd, encoding='utf-8')\n    cells_by_name, cells_by_uuid = parse_list_cells(list_cells_output)\n\n    if CELL0_ID in cells_by_uuid:\n        LOG.info('Setting cell0 database connection to \\'{}\\''.format(cell0_db))\n        cmd = [\n            '/usr/bin/nova-manage', 'cell_v2', 'update_cell',\n            '--cell_uuid', CELL0_ID,\n            '--database_connection', cell0_db,\n            '--transport-url', 'none:///'\n        ]\n    else:\n        LOG.info('Creating cell0 with database connection \\'{}\\''.format(cell0_db))\n        cmd = [\n            '/usr/bin/nova-manage', 'cell_v2', 'map_cell0',\n             '--database_connection', cell0_db\n        ]\n    subprocess.check_call(cmd)\n\n    if DEFAULT_CELL_NAME in cells_by_name:\n        LOG.info('Setting default cell database connection to \\'{}\\' and transport url to \\'{}\\''.format(\n            default_db, default_transport_url))\n        cmd = [\n            '/usr/bin/nova-manage', 'cell_v2', 'update_cell',\n            '--cell_uuid', cells_by_name[DEFAULT_CELL_NAME]['uuid'],\n            '--database_connection', default_db,\n            '--transport-url', default_transport_url\n        ]\n    else:\n        LOG.info('Creating default cell with database connection \\'{}\\' and transport url \\'{}\\''.format(\n            default_db, default_transport_url))\n        cmd = [\n            '/usr/bin/nova-manage', 'cell_v2', 'create_cell',\n            '--name', DEFAULT_CELL_NAME,\n             '--database_connection', default_db,\n            '--transport-url', default_transport_url\n        ]\n    subprocess.check_call(cmd)\n\n\ndef replace_db_name(db_url, db_name):\n    return urlparse.urlparse(db_url)._replace(path=db_name).geturl()\n\n\nif __name__ == '__main__':\n    if os.path.isfile(NOVA_CFG):\n        try:\n            config.read(NOVA_CFG)\n        except Exception:\n            LOG.exception('Error while reading nova.conf:')\n            sys.exit(1)\n    else:\n        LOG.error('Nova configuration file %s does not exist', NOVA_CFG)\n        sys.exit(1)\n\n    default_database_connection = config.get('database', 'connection')\n    cell0_database_connection = replace_db_name(default_database_connection, 'nova_cell0')\n    default_transport_url = config.get('DEFAULT', 'transport_url')\n\n    create_or_update_default_cells(\n        template_url(cell0_database_connection),\n        template_url(default_database_connection),\n        template_url(default_transport_url)\n    )\n"}}, "tuple:('role_data', 'docker_config')": {"step_2": {"nova_api_init_logs": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "step_3": {"nova_api_db_sync": {"start_order": 0, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "cap_add": ["AUDIT_WRITE"], "net": "host", "detach": false, "user": "root", "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_api_db_sync.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS", "TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}, "nova_api_ensure_default_cells": {"start_order": 1, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "user": "root", "net": "host", "privileged": false, "detach": false, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_api_ensure_default_cells.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro", "/var/lib/container-config-scripts:/container-config-scripts:z"], "environment": {"__OS_DEBUG": "true", "KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}, "step_4": {"nova_api": {"start_order": 2, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "host", "user": "root", "privileged": false, "restart": "always", "healthcheck": {"test": "/openstack/healthcheck"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_api.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro", "/etc/pki/tls/certs/httpd:/etc/pki/tls/certs/httpd:ro", "/etc/pki/tls/private/httpd:/etc/pki/tls/private/httpd:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}, "nova_wait_for_api_service": {"start_order": 3, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "user": "root", "net": "host", "privileged": false, "detach": false, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_wait_for_api_service.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro", "/var/lib/container-config-scripts:/container-config-scripts:z"], "environment": {"__OS_DEBUG": "true", "KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}, "nova_api_cron": {"start_order": 4, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "host", "user": "root", "privileged": false, "restart": "always", "healthcheck": {"test": "/usr/share/openstack-tripleo-common/healthcheck/cron nova"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_api_cron.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "tuple:('role_data', 'service_name')": "nova_api", "role_data": {"service_name": "nova_api", "firewall_rules": {"113 nova_api": {"dport": [8774]}}, "firewall_frontend_rules": {"100 nova_osapi_haproxy_frontend": {"dport": [8774]}}, "firewall_ssl_frontend_rules": {"100 nova_osapi_haproxy_frontend_ssl": {"dport": [13774]}}, "keystone_resources": {"nova": {"endpoints": {"public": "https://standalone.ooo.test:13774/v2.1", "internal": "https://overcloud.internalapi.ooo.test:8774/v2.1", "admin": "https://overcloud.internalapi.ooo.test:8774/v2.1"}, "users": {"nova": {"roles": ["admin", "service"], "password": "ksmPDM6JveiqLvoFg56EAxny1"}}, "region": "regionOne", "service": "compute"}}, "monitoring_subscription": "overcloud-nova-api", "config_settings": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "apache::default_vhost": false, "nova::keystone::authtoken::project_name": "service", "nova::keystone::authtoken::user_domain_name": "Default", "nova::keystone::authtoken::project_domain_name": "Default", "nova::keystone::authtoken::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::authtoken::www_authenticate_uri": "https://standalone.ooo.test:13000", "nova::keystone::authtoken::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::authtoken::region_name": "regionOne", "nova::keystone::authtoken::interface": "internal", "nova::keystone::authtoken::memcache_use_advanced_pool": true, "nova::api::max_limit": 1000, "nova::api::enabled": true, "nova::network::neutron::default_floating_pool": "public", "nova::api::enable_proxy_headers_parsing": true, "nova::api::api_bind_address": "%{hiera('fqdn_internal_api')}", "nova::api::service_name": "httpd", "nova::wsgi::apache_api::ssl": true, "nova::wsgi::apache_api::bind_host": "%{hiera('internal_api')}", "nova::wsgi::apache_api::servername": "%{hiera('fqdn_internal_api')}", "nova::api::instance_name_template": "instance-%08x", "nova::policy::policies": {}, "nova::api::allow_resize_to_same_host": false, "nova_enable_db_purge": true, "nova::cron::purge_shadow_tables::minute": "0", "nova::cron::purge_shadow_tables::hour": "5", "nova::cron::purge_shadow_tables::monthday": "*", "nova::cron::purge_shadow_tables::month": "*", "nova::cron::purge_shadow_tables::weekday": "*", "nova::cron::purge_shadow_tables::user": "nova", "nova::cron::purge_shadow_tables::destination": "/var/log/nova/nova-rowspurge.log", "nova::cron::purge_shadow_tables::maxdelay": "3600", "nova::cron::purge_shadow_tables::age": 14, "nova::cron::purge_shadow_tables::verbose": false, "nova::cron::purge_shadow_tables::all_cells": true, "nova_enable_db_archive": true, "nova::cron::archive_deleted_rows::minute": "1", "nova::cron::archive_deleted_rows::hour": "0", "nova::cron::archive_deleted_rows::monthday": "*", "nova::cron::archive_deleted_rows::month": "*", "nova::cron::archive_deleted_rows::weekday": "*", "nova::cron::archive_deleted_rows::max_rows": "1000", "nova::cron::archive_deleted_rows::user": "nova", "nova::cron::archive_deleted_rows::destination": "/var/log/nova/nova-rowsflush.log", "nova::cron::archive_deleted_rows::maxdelay": "3600", "nova::cron::archive_deleted_rows::until_complete": true, "nova::cron::archive_deleted_rows::purge": false, "nova::cron::archive_deleted_rows::all_cells": true, "nova::cron::archive_deleted_rows::age": 90, "nova::policy::enforce_new_defaults": false, "nova::policy::enforce_scope": false, "nova::api::osapi_compute_workers": 1, "nova::wsgi::apache_api::workers": 1}, "service_config_settings": {"rabbitmq": {"get_attr": ["NovaBase", "role_data", "service_config_settings"], "rabbitmq": null}, "mysql": {"nova::db::mysql_api::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql_api::user": "nova_api", "nova::db::mysql_api::host": "%", "nova::db::mysql_api::dbname": "nova_api", "nova::db::mysql::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql::user": "nova", "nova::db::mysql::host": "%", "nova::db::mysql::dbname": "nova"}, "rsyslog": {"tripleo_logging_sources_nova_api": [{"tag": "openstack.nova.api", "file": "/var/log/containers/nova/nova-api.log"}]}}, "puppet_config": {"config_volume": "nova", "puppet_tags": "nova_config,nova_api_paste_ini", "step_config": "['Nova_cell_v2'].each |String $val| { noop_resource($val) }\ninclude tripleo::profile::base::nova::api\ninclude tripleo::profile::base::database::mysql::client", "config_image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}, "kolla_config": {"/var/lib/kolla/config_files/nova_api.json": {"command": "/usr/sbin/httpd -DFOREGROUND", "config_files": [{"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.d", "dest": "/etc/httpd/conf.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.modules.d", "dest": "/etc/httpd/conf.modules.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}, "/var/lib/kolla/config_files/nova_api_db_sync.json": {"command": "/usr/bin/bootstrap_host_exec nova_api su nova -s /bin/bash -c '/usr/bin/nova-manage api_db sync'", "config_files": [{"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.d", "dest": "/etc/httpd/conf.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.modules.d", "dest": "/etc/httpd/conf.modules.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}, "/var/lib/kolla/config_files/nova_api_ensure_default_cells.json": {"command": "/usr/bin/bootstrap_host_exec nova_api su nova -s /bin/bash -c '/container-config-scripts/pyshim.sh /container-config-scripts/nova_api_ensure_default_cells.py'", "config_files": [{"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.d", "dest": "/etc/httpd/conf.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.modules.d", "dest": "/etc/httpd/conf.modules.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}, "/var/lib/kolla/config_files/nova_api_cron.json": {"command": "/usr/sbin/crond -n", "config_files": [{"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}, "/var/lib/kolla/config_files/nova_wait_for_api_service.json": {"command": "/usr/bin/bootstrap_host_exec nova_api su nova -s /bin/bash -c '/container-config-scripts/pyshim.sh /container-config-scripts/nova_wait_for_api_service.py'", "config_files": [{"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.d", "dest": "/etc/httpd/conf.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.modules.d", "dest": "/etc/httpd/conf.modules.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}}, "container_config_scripts": {"container_puppet_apply.sh": {"mode": "0700", "content": "#!/bin/bash\nset -eux\nSTEP=$1\nTAGS=$2\nCONFIG=$3\nEXTRA_ARGS=${4:-''}\nif [ -d /tmp/puppet-etc ]; then\n  # ignore copy failures as these may be the same file depending on docker mounts\n  cp -a /tmp/puppet-etc/* /etc/puppet || true\nfi\necho \"{\\\"step\\\": ${STEP}}\" > /etc/puppet/hieradata/docker_puppet.json\n# $::deployment_type in puppet-tripleo\nexport FACTER_deployment_type=containers\nset +e\npuppet apply $EXTRA_ARGS \\\n    --verbose \\\n    --detailed-exitcodes \\\n    --summarize \\\n    --color=false \\\n    --modulepath /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules \\\n    --tags $TAGS \\\n    -e \"noop_resource('package'); ${CONFIG}\"\nrc=$?\nset -e\nset +ux\nif [ $rc -eq 2 -o $rc -eq 0 ]; then\n    exit 0\nfi\nexit $rc\n"}, "pyshim.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n#\n# Usage: pyshim.sh <script and/or arguments>\n#\n# Unfortunately THT doesn't know which version of python might be in a\n# container so we need this script to be able to try python3 or python2\n# depending on availability.  Since this is a temporary shim until we've\n# fully cut over to python3, we check for the existence of python3 first\n# before falling back to python2. This will help in the transition from\n# python2 based containers to python3.\n\nshow_usage() {\n    echo \"Usage: pyshim.sh <script and/or arguments>\"\n}\n\nif [ $# -lt 1 ]\nthen\n    show_usage\n    exit 1\nfi\n\nset -x\nif command -v python3 >/dev/null; then\n    python3 \"$@\"\nelif command -v python2 >/dev/null; then\n    python2 \"$@\"\nelif command -v python >/dev/null; then\n    python \"$@\"\nelse\n    echo \"ERROR: python is not available!\"\n    exit 1\nfi\n"}, "pacemaker_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nset -u\n\n# ./pacemaker_restart_bundle.sh mysql galera galera-bundle Master _\n# ./pacemaker_restart_bundle.sh redis redis redis-bundle Slave Master\n# ./pacemaker_restart_bundle.sh ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\nRESTART_SCRIPTS_DIR=$(dirname $0)\nTRIPLEO_SERVICE=$1\nRESOURCE_NAME=$2\nBUNDLE_NAME=$3\nWAIT_TARGET_LOCAL=$4\nWAIT_TARGET_ANYWHERE=${5:-_}\nTRIPLEO_MINOR_UPDATE=\"${TRIPLEO_MINOR_UPDATE:-false}\"\nTRIPLEO_HA_WRAPPER_RESOURCE_EXISTS=\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS:-false}\"\n\nbundle_can_be_restarted() {\n    local bundle=$1\n    # As long as the resource bundle is managed by pacemaker and is\n    # not meant to stay stopped, no matter the state of any inner\n    # pcmk_remote or ocf resource, we should restart it to give it a\n    # chance to read the new config.\n    [ \"$(crm_resource --meta -r $1 -g is-managed 2>/dev/null)\" != \"false\" ] && \\\n    [ \"$(crm_resource --meta -r $1 -g target-role 2>/dev/null)\" != \"Stopped\" ]\n}\n\nlog() {\n    local msg=$1\n    logger -t pcmkrestart \"$1\"\n}\n\nHOSTNAME=$(/bin/hostname -s)\nif [ x\"${TRIPLEO_MINOR_UPDATE,,}\" != x\"true\" ]; then\n    if [ x\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS,,}\" = x\"false\" ]; then\n        # Do not restart during initial deployment, as the resource\n        # has just been created.\n        SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n        if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n            log \"Initial deployment, skipping the restart of ${BUNDLE_NAME}\"\n\tfi\n        exit 0\n    else\n        # During a stack update, this script is called in parallel on\n        # every node the resource runs on, after the service's configs\n        # have been updated on all nodes. So we need to run pcs only\n        # once (e.g. on the service's boostrap node).\n        if bundle_can_be_restarted ${BUNDLE_NAME}; then\n            SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n            if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n                replicas_running=$(crm_resource -Q -r $BUNDLE_NAME --locate 2>&1 | wc -l)\n                if [ \"$replicas_running\" != \"0\" ]; then\n                    log \"Restarting ${BUNDLE_NAME} globally. Stopping:\"\n                    /sbin/pcs resource disable --wait=600 $BUNDLE_NAME\n                    log \"Restarting ${BUNDLE_NAME} globally. Starting:\"\n                    /sbin/pcs resource enable --wait=600 $BUNDLE_NAME\n                else\n                    log \"${BUNDLE_NAME} is not running anywhere,\" \\\n                         \"cleaning up to restart it globally if necessary\"\n                    /sbin/pcs resource cleanup $BUNDLE_NAME\n                fi\n            else\n                log \"Skipping global restart of ${BUNDLE_NAME} on ${HOSTNAME} it will be restarted by node ${SERVICE_NODEID}\"\n            fi\n\n        else\n            log \"No global restart needed for ${BUNDLE_NAME}.\"\n        fi\n    fi\nelse\n    # During a minor update workflow however, a host gets fully\n    # updated before updating the next one. So unlike stack\n    # update, at the time this script is called, the service's\n    # configs aren't updated on all nodes yet. So only restart the\n    # resource locally, where it's guaranteed that the config is\n    # up to date.\n    HOST=$(facter hostname)\n\n    if bundle_can_be_restarted ${BUNDLE_NAME}; then\n\t# if the resource is running locally, restart it\n\tif crm_resource -r $BUNDLE_NAME --locate 2>&1 | grep -w -q \"${HOST}\"; then\n            log \"Restarting ${BUNDLE_NAME} locally on '${HOST}'\"\n            /sbin/pcs resource restart $BUNDLE_NAME \"${HOST}\"\n\n\telse\n\t    # At this point, if no resource is running locally, it's\n\t    # either because a) it has failed previously, or b) because\n\t    # it's an A/P resource running elsewhere.\n\t    # By cleaning up resource, we ensure that a) it will try to\n\t    # restart, or b) it won't do anything if the resource is\n\t    # already running elsewhere.\n            log \"${BUNDLE_NAME} is currently not running on '${HOST}',\" \\\n                 \"cleaning up its state to restart it if necessary\"\n            /sbin/pcs resource cleanup $BUNDLE_NAME node=\"${HOST}\"\n\tfi\n\n\t# Wait until the resource is in the expected target state\n\t$RESTART_SCRIPTS_DIR/pacemaker_wait_bundle.sh \\\n            $RESOURCE_NAME $BUNDLE_NAME \\\n            \"$WAIT_TARGET_LOCAL\" \"$WAIT_TARGET_ANYWHERE\" \\\n\t    \"${HOST}\" 600\n    else\n        log \"No restart needed for ${BUNDLE_NAME}.\"\n    fi\nfi\n"}, "pacemaker_wait_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# ----\n# Wait for an OCF resource or a bundle to be restarted\n# ----\n# e.g.:\n# M/S OCF:      $0 galera galera-bundle Master\n# clone OCF:    $0 rabbitmq rabbitmq-bundle Started\n# A/P M/S OCF:  $0 redis redis-bundle Slave Master\n# A/P bundle:   $0 openstack-cinder-volume openstack-cinder-volume _ Started\n# clone bundle: $0 haproxy-bundle haproxy-bundle Started\n\n# design note 1:\n#  - this script is called during a minor update; it is called\n#    once per node that hosts a service replica.\n#  - the purpose of this script is to ensure that restarting the\n#    service replica locally won't disrupt the service availability\n#    for the end user. To reach that goal, the script waits until the\n#    service is restarted locally or globallu and reaches a given\n#    target state (i.e. Started, Slave or Master).\n# design note 2:\n#   - we don't want to track restart error: our only job is to ensure\n#     service restart synchronization, not service health.\n#   - In particular, we don't want to error out in case the resource\n#     cannot be restarted locally, because that would make the minor\n#     update fail, even if potentially other replicas still provide\n#     the service.\n# design note 3:\n#   - we can bail out early if we determine that the resource can't\n#     be restarted automatically by pacemaker (e.g. its \"blocked\",\n#     unmanaged or disabled).\n\nlog() {\n    local msg=$1\n    echo \"$(date -u): $1\"\n}\n\nusage() {\n    echo 2>&1 \"Usage: $0 NAME BUNDLE_NAME ROLE_LOCAL [ROLE_ANYWHERE] [HOST] [TIMEOUT]\"\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\n#\n# Utility functions to detect stuck resources\n#\n\nbundle_failures_locally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local replica_name\n    for i in $(seq 0 $last); do\n\treplica_name=${BUNDLE_NAME}-${engine}-${i}\n\tcrm_failcount -q -G -r $replica_name -N $HOST\n    done\n}\n\nbundle_failures_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    for i in $(seq 0 $last); do\n\tcrm_failcount -q -G -r ${BUNDLE_NAME}-${engine}-${i}\n    done\n}\n\nbundle_running_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    # return the number of running bundles replica, i.e. the number of\n    # docker/podman resource replicas currently running in the cluster\n    crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:heartbeat:${engine}']/node)\" -\n}\n\nocf_failures_globally() {\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local bundle_node\n    for i in $(seq 0 $last); do\n\tbundle_node=${BUNDLE_NAME}-${i}\n\tcrm_failcount -q -G -r $NAME -N $bundle_node\n    done\n}\n\ndid_resource_failed_locally() {\n    local failures\n    local running\n    local remotehost\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# if we're dealing with an ocf resource, it is running on a\n\t# pacemaker_remote rather that on the real host, and the\n\t# failcounts are thus associated to the pcmk remote. Replace\n\t# the host's name with the pcmk remote's name.\n        remotehost=$(crm_mon --as-xml | xmllint --xpath \"string(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource/node[@name='${HOST}']/../../resource[@resource_agent='${OCF}:pacemaker:remote']/@id)\" -)\n\tif [ -n \"${remotehost}\" ]; then\n\t    crm_failcount -q -G -r $NAME -N $remotehost | grep -q -w INFINITY\n\t    return $?\n\tfi\n\t# If no pcmk remote is currently running, the failcount from\n\t# the ocf resource is useless, compute the failcount from the\n\t# bundle case instead (computed below).\n    fi\n\n    # for bundles, pacemaker can run any bundle replica locally\n    # (e.g. galera-bundle-docker-{0,1,2}), and a failure happens when\n    # there are no more replica to try.\n    # That is, when _at least_ one replica failed locally, and all the\n    # others either failed or are currently running elsewhere.\n    failures=$(bundle_failures_locally $HOST | grep -c -w INFINITY)\n    running=$(bundle_running_globally)\n    test $failures -gt 0 && \\\n    test $(( $failures + $running )) -ge $BUNDLE_REPLICAS\n}\n\ndid_resource_failed_globally() {\n    local remotecount\n    local failures\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# we check the state of an ocf resource only if the\n\t# pcmkremotes are started\n        remotecount=$(crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node)\" -)\n\tif [ \"${remotecount}\" = \"0\" ]; then\n\t    # no pcmkremote is running, so check the bundle state\n\t    # instead of checking the ocf resource\n\t    # bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\t    failures=$(bundle_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\telse\n\t    # ocf resource failed if it failed to start on\n\t    # all $BUNDLE_REPLICAS bundle nodes\n\t    failures=$(ocf_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\tfi\n    else\n\t# bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\tfailures=$(bundle_failures_globally | grep -c -w INFINITY)\n\ttest $failures -eq $BUNDLE_REPLICAS\n    fi\n}\n\n\n# Input validation\n#\n\nNAME=$1\nif [ -z \"${NAME}\" ]; then\n    echo 2>&1 \"Error: argument NAME must not be empty\"\n    exit 1\nfi\n\nBUNDLE_NAME=$2\nif [ -z \"${BUNDLE_NAME}\" ]; then\n    echo 2>&1 \"Error: argument BUNDLE_NAME must not be empty\"\n    exit 1\nfi\n\nROLE_LOCAL=$3\nif [ \"${ROLE_LOCAL}\" = \"_\" ]; then\n    ROLE_LOCAL=\"\"\nfi\n\nROLE_ANYWHERE=$4\nif [ \"${ROLE_ANYWHERE}\" = \"_\" ]; then\n    ROLE_ANYWHERE=\"\"\nfi\n\nif [ -z \"${ROLE_LOCAL}\" ]; then\n    if [ -z \"${ROLE_ANYWHERE}\" ]; then\n        echo 2>&1 \"Error: either ROLE_LOCAL or ROLE_ANYWHERE must be non empty\"\n        exit 1\n    fi\nelse\n    if !(echo \"${ROLE_LOCAL}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n        echo 2>&1 \"Error: argument ROLE_LOCAL must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n        exit 1\n    fi\nfi\n\nif [ -n \"${ROLE_ANYWHERE}\" ] && !(echo \"${ROLE_ANYWHERE}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n    echo 2>&1 \"Error: argument ROLE_ANYWHERE must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n    exit 1\nfi\n\n# Ensure compatibility with pacemaker 2.1\nif pacemaker_supports_promoted; then\n    ROLE_LOCAL=$(echo \"$ROLE_LOCAL\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    ROLE_ANYWHERE=$(echo \"$ROLE_ANYWHERE\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    OCF=\"ocf\"\nelse\n    OCF=\"ocf:\"\nfi\n\nHOST=${5:-$(facter hostname)}\nTIMEOUT=${6:-600}\n\n\n# Configure the search\n# ----\n# Note: we can't use crm_resource in all searches because we can't\n# easily extract the host the OCF resources run on (crm_resource\n# returns the pcmk-remote nodes rather than the hosts)\n# So instead, we implement various searches with XPath directly.\n\nif [ \"${BUNDLE_NAME}\" != \"${NAME}\" ]; then\n# ocf resource\nlocal_resource_xpath=\"//bundle/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node[@name='${HOST}']/../../resource[@id='${NAME}']\"\nany_resource_xpath=\"//bundle//resource[@id='${NAME}']\"\nreplicas_xpath=\"//bundle/primitive[@id='${BUNDLE_NAME}']/../*[boolean(@image) and boolean(@replicas)]\"\nelse\n# bundle resource\nlocal_resource_xpath=\"//bundle[@id='${NAME}']/replica/resource/node[@name='${HOST}']/../../resource\"\nany_resource_xpath=\"//bundle[@id='${NAME}']//resource\"\nreplicas_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nfi\n\nbundle_def_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nBUNDLE_CONTAINER_ENGINE=$(cibadmin -Q | xmllint --xpath \"name(${bundle_def_xpath})\" -)\nBUNDLE_REPLICAS=$(cibadmin -Q | xmllint --xpath \"string(${bundle_def_xpath}/@replicas)\" -)\n\n\n# The wait algorithm follows a two-stage approach\n#  1. Depending on how the script is called, we first check whether\n#     the resource is restarted locally. An A/P resource may be\n#     restarted elsewhere in the cluster.\n#  2. If needed, check whether the A/P resource has restarted\n#     elsewhere. For A/P M/S resources, in case the resource is\n#     restarted as Slave locally, ensure a Master is available.\n\nsuccess=1\nbailout=1\ntimeout=$TIMEOUT\nrole=\"\"\n\n# Stage 1: local check\nif [ -n \"$ROLE_LOCAL\" ]; then\n    log \"Waiting until ${NAME} has restarted on ${HOST} and is in state ${ROLE_LOCAL}\"\n    log \"Will probe resource state with the following XPath pattern: ${local_resource_xpath}\"\n\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resource=$(crm_mon -r --as-xml | xmllint --xpath \"${local_resource_xpath}\" - 2>/dev/null)\n        role=$(echo \"${resource}\" | sed -ne 's/.*\\Wrole=\"\\([^\"]*\\)\".*/\\1/p')\n\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resource}\" | grep -q -w \"\\Wblocked=\\\"true\\\"\"; then\n            log \"${NAME} is blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_locally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif [ \"$role\" = \"$ROLE_LOCAL\" ]; then\n            success=0\n            continue\n        elif [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" = \"$ROLE_ANYWHERE\" ]; then\n            # A/P: we are restarted in the expected state\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_LOCAL} on ${HOST}\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\n# Stage 2: global check\nif [ $timeout -gt 0 ] && [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" != \"$ROLE_ANYWHERE\" ]; then\n    log \"Wait until ${NAME} is restarted anywhere in the cluster in state ${ROLE_ANYWHERE}\"\n    log \"Will probe resource state with the following XPath pattern: ${any_resource_xpath}\"\n\n    success=1\n    bailout=1\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resources=$(crm_mon -r --as-xml | xmllint --xpath \"${any_resource_xpath}\" - 2>/dev/null)\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif ! (echo \"${resources}\" | grep -q -w \"\\Wblocked=\\\"false\\\"\"); then\n            log \"${NAME} blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_globally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resources}\" | grep -q -w \"\\Wrole=\\\"${ROLE_ANYWHERE}\\\"\"; then\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_ANYWHERE} anywhere in the cluster\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\nif [ $timeout -le 0 ]; then\n    log \"Timeout reached after ${TIMEOUT}s while waiting for ${NAME} to be restarted\"\nelif [ $bailout -le 0 ]; then\n    log \"Restart monitoring for ${NAME} cancelled\"\nfi\n\nif [ $success -eq 0 ]; then\n    log \"${NAME} successfully restarted\"\nelse\n    log \"${NAME} was not restarted properly\"\nfi\n\n# Don't block minor update or stack update if the wait was unsuccessful\nexit 0\n"}, "wait-port-and-run.sh": {"mode": "0755", "content": "#!/usr/bin/bash\nset -eu\n\nHOST=$1\nPORT=$2\n\necho \"$(date -u): Checking whether we can bind to ${HOST}:${PORT}\"\nwhile (ss -Htnl src \"${HOST}\" \"sport = :${PORT}\" | grep -wq \"${PORT}\"); do\n    echo \"$(date -u): ${HOST}:${PORT} still in use, waiting...\";\n    sleep 10;\ndone\n\nshift 2\nCOMMAND=\"$*\"\nif [ -z \"${COMMAND}\" ]; then\n    COMMAND=\"true\"\nfi\nexec $COMMAND\n"}, "pacemaker_resource_lock.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nMAX_RETRIES=10\nCIB_ENOTFOUND=105\n\nusage() {\n   echo \"Set a global property in the cluster with a validity timestamp.\"\n   echo \"Usage:\"\n   echo \"   $0 --acquire <lock_name> <lock_owner> <lock_ttl_in_seconds>\"\n   echo \"   $0 --release <lock_name> <lock_owner>\"\n   echo\n}\n\nlog() {\n    echo \"$(date -u): $1\" 1>&2\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\nlock_create() {\n    local name=$1\n    local data=$2\n    # cibadmin won't overwrite a key if someone else succeeded to create it concurrently\n    cibadmin --sync-call --scope crm_config --create --xml-text \"<cluster_property_set id='${name}'><nvpair id='${name}-pair' name='${name}' value='${data}'/></cluster_property_set>\" &>/dev/null\n    return $?\n}\n\nlock_update() {\n    local name=$1\n    local expected_data=$2\n    local new_data=$3\n    # we only update the lock we expect to see, so we can't update someone else's lock\n    cibadmin --sync-call --scope crm_config --modify --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" --xml-text \"<nvpair id='${name}-pair' name='${name}' value='${new_data}'/>\" &>/dev/null\n    return $?\n}\n\nlock_delete() {\n    local name=$1\n    local expected_data=$2\n    # we only delete the lock we expect to see, so we can't delete someone else's lock\n    cibadmin --sync-call --scope crm_config --delete --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" &>/dev/null\n    return $?\n}\n\nlock_get() {\n    local lockname=$1\n    local res\n    local rc\n    res=$(cibadmin --query --scope crm_config --xpath \"//cluster_property_set/nvpair[@name='$lockname']\" 2>/dev/null)\n    rc=$?\n    if [ $rc -eq 0 ]; then\n        echo \"$res\" | sed -n 's/.*value=\"\\([^\"]*\\)\".*/\\1/p'\n    fi\n    return $rc\n}\n\nlock_owner() {\n    local lock=$1\n    echo \"$lock\" | cut -d':' -f1\n}\n\nlock_has_expired() {\n    local lock=$1\n    local expiry=$(echo \"$lock\" | cut -d':' -f2)\n    local now=$(date +%s)\n    test $now -ge $expiry\n}\n\n\n# Perform a lock action and restart if the CIB has been modified before\n# committing the lock action\ntry_action() {\n    local fun=$1\n    local lock=$2\n    local requester=$3\n    local args=${4:-}\n    local tries=$MAX_RETRIES\n    local rc=1\n    if [ \"$fun\" = \"lock_acquire\" ] || [ \"$fun\" = \"lock_release\" ]; then\n        log \"Try running $fun\"\n    else\n        return 2\n    fi\n    while [ $rc -ne 0 ]; do\n        $fun $lock $requester $args\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Operation $1 succeeded\"\n            return 0\n        elif [ $rc -eq 3 ]; then\n            # rc == 3 -> CIB changed before push\n            if [ $tries -eq 0 ]; then\n                log \"Failed to commit after $MAX_RETRIES retries. Bailing out.\"\n                return 2\n            else\n                log \"Failed to commit. Retrying operation.\"\n                tries=$(($tries - 1))\n            fi\n        elif [ $rc -eq 2 ]; then\n            # rc == 2 -> unrecoverable cib error (e.g. pacemaker down)\n            log \"Unexpected failure. Bailing out\"\n            return $rc\n        else\n            # rc == 1 -> lock error (not owner, lock doesn't exists)\n            return $rc\n        fi\n    done\n}\n\n# The lock mechanism uses cibadmin's atomic creation so cluster-wide\n# state coherency is guaranteed by pacemaker\nlock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc\n    local lock\n    local expiry\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -n \"$lock\" ]; then\n        lock_has_expired $lock\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Lock has expired, now available for being held\"\n        else\n            # lock is still held. check whether we're the owner\n            owner=$(lock_owner $lock)\n            if [ \"$owner\" = \"$requester\" ];then\n                log \"Requester already owns the lock, acquiring attempt will just reconfigure the TTL\"\n            else\n                log \"Lock is held by someone else ($owner)\"\n                return 1\n            fi\n        fi\n    else\n        log \"Lock is not held yet\"\n    fi\n\n    # prepare the lock info\n    expiry=$(($(date +%s) + $ttl))\n\n    if [ -n \"$lock\" ]; then\n        log \"Attempting to update the lock\"\n        lock_update $lockname \"$lock\" \"$requester:$expiry\"\n        rc=$?\n    else\n        log \"Attempting to acquire the lock\"\n        lock_create $lockname \"$requester:$expiry\"\n        rc=$?\n    fi\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' acquired by '$requester', valid until $(date -d @$expiry)\"\n        return 0\n    else\n        log \"CIB changed, lock cannot be acquired\"\n        return 3\n    fi\n}\n\n\n# The lock mechanism uses the CIB's num_updates tag to implement\n# a conditional store. Cluster-wide locking is guaranteed by pacemaker\nlock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n    local lock\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        log \"Lock doesn't exist. Nothing to release\"\n        return 0\n    else\n        log \"Lock exists, check whether we're the owner\"\n        owner=$(lock_owner $lock)\n        if [ \"$owner\" != \"$requester\" ];then\n            log \"Lock is held by someone else ($owner), will not unlock\"\n            return 1\n        fi\n    fi\n\n    lock_delete $lockname \"$lock\"\n    rc=$?\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' released by '$requester'\"\n        return 0\n    else\n        log \"CIB deletion error, lock cannot be released\"\n        return 3\n    fi\n}\n\n\n# Retrieve the owner of a lock from the CIB\n# this is a read-only operation, so no need to log debug info\nlock_get_owner() {\n    local lockname=$1\n    local rc\n    local lock\n    local owner\n\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        return 1\n    else\n        lock_owner $lock\n        return 0\n    fi\n}\n\n\nACTION=$1\nLOCKNAME=$2\nREQUESTER=$3\nTTL=${4:-60}\n\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nif [ $ACTION != \"--help\" ]; then\n    if [ -z \"$LOCKNAME\" ]; then\n        error \"You must specific a lock name\"\n    fi\n    if [ $ACTION != \"--owner\" ] && [ $ACTION != \"-o\" ]; then\n        if [ -z \"$REQUESTER\" ]; then\n            error \"You must specific a lock requester\"\n        fi\n    fi\nfi\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) try_action lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --release|-r) try_action lock_release $LOCKNAME $REQUESTER;;\n    --acquire-once|-A) lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --owner|-o) lock_get_owner $LOCKNAME;;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "pacemaker_mutex_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_restart_bundle.sh --lock mysql galera galera-bundle Master _\n# pacemaker_mutex_restart_bundle.sh --lock ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\n\nset -u\n\nusage() {\n    echo \"Restart a clustered resource in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --lock <tripleo-service> <pcmk-resource> <pcmk-bundle> <target-state-local> <target-state-cluster>\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\nACTION=$1\ncase $ACTION in\n    --help) usage; exit 0;;\n    --lock) ;;\n    *) error \"Unknown action '$ACTION'\";;\nesac\n\nTRIPLEO_SERVICE=$2\nLOCK_NAME=${TRIPLEO_SERVICE}-restart-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to restart resource $TRIPLEO_SERVICE\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\nRESOURCE_NAME=$3\nBUNDLE_NAME=$4\nWAIT_TARGET_LOCAL=$5\nWAIT_TARGET_ANYWHERE=${6:-_}\n\nif pacemaker_supports_promoted; then\n    WAIT_TARGET_LOCAL=$(echo \"$5\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    WAIT_TARGET_ANYWHERE=$(echo \"${6:-_}\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    promoted_role=\"Promoted\"\nelse\n    promoted_role=\"Master\"\nfi\n\n# The lock TTL should accomodate for the resource start/promote timeout\nif [ \"$RESOURCE_NAME\" != \"$BUNDLE_NAME\" ]; then\n    if [ \"$WAIT_TARGET_LOCAL\" = \"$promoted_role\" ] || [ \"$WAIT_TARGET_ANYWHERE\" = \"$promoted_role\" ]; then\n        rsc_op=\"promote\"\n    else\n        rsc_op=\"start\"\n    fi\n    # <op id=\"galera-promote-interval-0s\" interval=\"0s\" name=\"promote\" on-fail=\"block\" timeout=\"300s\"/>\n    PCMK_TTL=$(cibadmin -Q | xmllint -xpath \"string(//primitive[@id='${RESOURCE_NAME}']/operations/op[@name='${rsc_op}']/@timeout)\" - | sed 's/s$//')\n    LOCK_TTL=$((PCMK_TTL + 30))\nelse\n    # The podman RA's default start timeout\n    LOCK_TTL=90\nfi\n\nlog \"Acquire a ${LOCK_TTL}s restart lock for service $TRIPLEO_SERVICE before restarting it\"\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nrc=1\nwhile [ $rc -ne 0 ]; do\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire $LOCK_NAME $LOCK_OWNER $LOCK_TTL\n    rc=$?\n    if [ $rc != 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not acquire lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Could not acquire lock, retrying\"\n            sleep 10\n        fi\n    fi\ndone\n\nlog \"Restart the service $TRIPLEO_SERVICE locally\"\n# Reuse the local restart script in t-h-t (driven by env var TRIPLEO_MINOR_UPDATE)\nTRIPLEO_MINOR_UPDATE=true /var/lib/container-config-scripts/pacemaker_restart_bundle.sh $TRIPLEO_SERVICE $RESOURCE_NAME $BUNDLE_NAME $WAIT_TARGET_LOCAL $WAIT_TARGET_ANYWHERE\n\n# If we reached this point, always try to release the lock\nlog \"Release the restart lock for service $TRIPLEO_SERVICE\"\n/var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $LOCK_NAME $LOCK_OWNER\nrc=$?\nif [ $rc -ne 0 ] && [ $rc -ne 1 ]; then\n    error \"Could not release held lock (rc: $rc)\"\nfi\n"}, "pacemaker_mutex_shutdown.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_shutdown.sh --acquire\n# pacemaker_mutex_shutdown.sh --release\n\nset -u\n\nusage() {\n    echo \"Shutdown a cluster node in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --acquire # prevent other node from shutting down until we hold the lock\"\n    echo \"   $0 --release # release the lock, other node can compete for the shutdown lock\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nshutdown_lock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc=1\n    local current_owner\n    local owner_stopped\n    local owner_rc\n\n    log \"Acquiring the shutdown lock\"\n    while [ $rc -ne 0 ]; do\n        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire-once $lockname $requester $ttl\n        rc=$?\n        if [ $rc -ne 0 ]; then\n            if [ $rc -eq 2 ]; then\n                error \"Could not acquire the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n            else\n                # The lock is held by another node.\n                current_owner=$(/var/lib/container-config-scripts/pacemaker_resource_lock.sh --owner $lockname)\n                owner_rc=$?\n                if [ $owner_rc -eq 2 ]; then\n                    error \"Could not get the shutdown lock owner due to unrecoverable error (rc: $owner_rc), bailing out\"\n                fi\n                if [ $owner_rc -eq 0 ]; then\n                    # If the owner is marked as offline, that means it has shutdown and\n                    # we can clean the lock preemptively and try to acquire it.\n                    owner_stopped=$(crm_mon -1X | xmllint --xpath 'count(//nodes/node[@name=\"'${current_owner}'\" and @online=\"false\" and @unclean=\"false\"])' -)\n                    if [ \"${owner_stopped}\" = \"1\" ]; then\n                        log \"Shutdown lock held by stopped node '${current_owner}', lock can be released\"\n                        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $current_owner\n                        continue\n                    fi\n                fi\n                log \"Shutdown lock held by another node (rc: $rc), retrying\"\n                sleep 10\n            fi\n        fi\n    done\n    log \"Shutdown lock acquired\"\n    return 0\n}\n\n\n# Release the lock if we still own it. Not owning it anymore is not fatal\nshutdown_lock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n\n    log \"Releasing the shutdown lock\"\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $requester\n    rc=$?\n    if [ $rc -ne 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not release the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Shutdown lock no longer held, nothing to do\"\n        fi\n    else\n        log \"Shutdown lock released\"\n    fi\n    return 0\n}\n\n\nACTION=$1\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nLOCK_NAME=tripleo-shutdown-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to aquire the shutdown lock\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\n# We start with a very high TTL, that long enough to accomodate a cluster stop.\n# As soon as the node will get offline, the other competing node will be entitled\n# to steal the lock, so they should never wait that long in practice.\nLOCK_TTL=600\n\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) shutdown_lock_acquire ${LOCK_NAME} ${LOCK_OWNER} ${LOCK_TTL};;\n    --release|-r) shutdown_lock_release ${LOCK_NAME} ${LOCK_OWNER};;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "nova_wait_for_api_service.py": {"mode": "0755", "content": "#!/usr/bin/python3\n#\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\nimport argparse\nimport logging\nimport os\nimport six\nimport sys\nimport time\n\n\nfrom keystoneauth1 import loading\nfrom keystoneauth1 import session\n\nfrom novaclient import client\nfrom novaclient.exceptions import ClientException\n\n# In python3 SafeConfigParser was renamed to ConfigParser and the default\n# for duplicate options default to true. In case of nova it is valid to\n# have duplicate option lines, e.g. passthrough_whitelist which leads to\n# issues reading the nova.conf\n# https://bugs.launchpad.net/tripleo/+bug/1827775\nif six.PY3:\n    from six.moves.configparser import ConfigParser\n    config = ConfigParser(strict=False)\nelse:\n    from six.moves.configparser import SafeConfigParser\n    config = SafeConfigParser()\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_wait_for_api_service')\n\niterations = 60\ntimeout = 10\nnova_cfg = '/etc/nova/nova.conf'\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(usage='%(prog)s [options]')\n    parser.add_argument('-k', '--insecure',\n                        action=\"store_false\",\n                        dest='insecure',\n                        default=True,\n                        help='Allow insecure connection when using SSL')\n\n    args = parser.parse_args()\n    LOG.debug('Running with parameter insecure = %s',\n              args.insecure)\n\n    if os.path.isfile(nova_cfg):\n        try:\n            config.read(nova_cfg)\n        except Exception:\n            LOG.exception('Error while reading nova.conf:')\n    else:\n        LOG.error('Nova configuration file %s does not exist', nova_cfg)\n        sys.exit(1)\n\n    loader = loading.get_plugin_loader('password')\n    auth = loader.load_from_options(\n        auth_url=config.get('neutron',\n                            'auth_url'),\n        username=config.get('neutron',\n                            'username'),\n        password=config.get('neutron',\n                            'password'),\n        project_name=config.get('neutron',\n                                'project_name'),\n        project_domain_name=config.get('neutron',\n                                       'project_domain_name'),\n        user_domain_name=config.get('neutron',\n                                    'user_domain_name'))\n    sess = session.Session(auth=auth, verify=args.insecure)\n\n    # Wait until this host is listed in the service list\n    for i in range(iterations):\n        try:\n            nova = client.Client('2.11', session=sess, endpoint_type='internal')\n            nova.versions.list()\n            LOG.info('Nova-api service active')\n            sys.exit(0)\n        except ClientException:\n            LOG.info('Waiting for nova-api service')\n        except Exception:\n            LOG.exception(\n                'Error while waiting for nova-api service')\n        time.sleep(timeout)\nsys.exit(1)\n\n# vim: set et ts=4 sw=4 :\n"}, "nova_api_ensure_default_cells.py": {"mode": "0755", "content": "#!/usr/bin/python3\n#\n# Copyright 2022 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\nfrom configparser import ConfigParser\nimport logging\nimport os\nimport subprocess\nimport sys\nfrom urllib import parse as urlparse\n\nconfig = ConfigParser(strict=False)\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_api_ensure_default_cells')\n\nNOVA_CFG = '/etc/nova/nova.conf'\nCELL0_ID = '00000000-0000-0000-0000-000000000000'\nDEFAULT_CELL_NAME = 'default'\n\n\ndef template_netloc_credentials(netloc, index=None):\n    if '@' in netloc:\n        userpass, hostport = netloc.split('@', 1)\n        has_pass = ':' in userpass\n        if index is None:\n            cred_template = '{username}'\n            if has_pass:\n                cred_template += ':{password}'\n        else:\n            cred_template = '{{username{index}}}'.format(index=index)\n            if has_pass:\n                cred_template += ':{{password{index}}}'.format(index=index)\n        return '@'.join((cred_template, hostport))\n    else:\n        return netloc\n\n\ndef template_url(url):\n    parsed = urlparse.urlparse(url)\n    if ',' in parsed.netloc:\n        orig_netlocs = parsed.netloc.split(',')\n        templ_netlocs = []\n        index = 0\n        for netloc in orig_netlocs:\n            index += 1\n            templ_netlocs.append(template_netloc_credentials(netloc, index))\n        new_netloc = ','.join(templ_netlocs)\n    else:\n        new_netloc = template_netloc_credentials(parsed.netloc)\n    return parsed._replace(netloc=new_netloc).geturl()\n\n\ndef parse_list_cells(list_cells_output):\n    list_cells_lines = list_cells_output.split('\\n')\n    if len(list_cells_lines) < 5:\n        raise ValueError('Invalid nova-manage cell_v2 list_cells output')\n\n    data_rows = list_cells_lines[3:-2]\n    by_name = {}\n    by_uuid = {}\n\n    for row in data_rows:\n        parts = row.split('|')\n        entry = {\n            'name': parts[1].strip(),\n            'uuid': parts[2].strip(),\n            'transport_url': parts[3].strip(),\n            'database_connection': parts[4].strip(),\n        }\n        by_name[entry['name']] = entry\n        by_uuid[entry['uuid']] = entry\n\n    return by_name, by_uuid\n\n\ndef create_or_update_default_cells(cell0_db, default_db, default_transport_url):\n    list_cells_cmd = ['/usr/bin/nova-manage', 'cell_v2', 'list_cells', '--verbose']\n    list_cells_output = subprocess.check_output(list_cells_cmd, encoding='utf-8')\n    cells_by_name, cells_by_uuid = parse_list_cells(list_cells_output)\n\n    if CELL0_ID in cells_by_uuid:\n        LOG.info('Setting cell0 database connection to \\'{}\\''.format(cell0_db))\n        cmd = [\n            '/usr/bin/nova-manage', 'cell_v2', 'update_cell',\n            '--cell_uuid', CELL0_ID,\n            '--database_connection', cell0_db,\n            '--transport-url', 'none:///'\n        ]\n    else:\n        LOG.info('Creating cell0 with database connection \\'{}\\''.format(cell0_db))\n        cmd = [\n            '/usr/bin/nova-manage', 'cell_v2', 'map_cell0',\n             '--database_connection', cell0_db\n        ]\n    subprocess.check_call(cmd)\n\n    if DEFAULT_CELL_NAME in cells_by_name:\n        LOG.info('Setting default cell database connection to \\'{}\\' and transport url to \\'{}\\''.format(\n            default_db, default_transport_url))\n        cmd = [\n            '/usr/bin/nova-manage', 'cell_v2', 'update_cell',\n            '--cell_uuid', cells_by_name[DEFAULT_CELL_NAME]['uuid'],\n            '--database_connection', default_db,\n            '--transport-url', default_transport_url\n        ]\n    else:\n        LOG.info('Creating default cell with database connection \\'{}\\' and transport url \\'{}\\''.format(\n            default_db, default_transport_url))\n        cmd = [\n            '/usr/bin/nova-manage', 'cell_v2', 'create_cell',\n            '--name', DEFAULT_CELL_NAME,\n             '--database_connection', default_db,\n            '--transport-url', default_transport_url\n        ]\n    subprocess.check_call(cmd)\n\n\ndef replace_db_name(db_url, db_name):\n    return urlparse.urlparse(db_url)._replace(path=db_name).geturl()\n\n\nif __name__ == '__main__':\n    if os.path.isfile(NOVA_CFG):\n        try:\n            config.read(NOVA_CFG)\n        except Exception:\n            LOG.exception('Error while reading nova.conf:')\n            sys.exit(1)\n    else:\n        LOG.error('Nova configuration file %s does not exist', NOVA_CFG)\n        sys.exit(1)\n\n    default_database_connection = config.get('database', 'connection')\n    cell0_database_connection = replace_db_name(default_database_connection, 'nova_cell0')\n    default_transport_url = config.get('DEFAULT', 'transport_url')\n\n    create_or_update_default_cells(\n        template_url(cell0_database_connection),\n        template_url(default_database_connection),\n        template_url(default_transport_url)\n    )\n"}}, "docker_config": {"step_2": {"nova_api_init_logs": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "step_3": {"nova_api_db_sync": {"start_order": 0, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "cap_add": ["AUDIT_WRITE"], "net": "host", "detach": false, "user": "root", "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_api_db_sync.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS", "TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}, "nova_api_ensure_default_cells": {"start_order": 1, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "user": "root", "net": "host", "privileged": false, "detach": false, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_api_ensure_default_cells.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro", "/var/lib/container-config-scripts:/container-config-scripts:z"], "environment": {"__OS_DEBUG": "true", "KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}, "step_4": {"nova_api": {"start_order": 2, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "host", "user": "root", "privileged": false, "restart": "always", "healthcheck": {"test": "/openstack/healthcheck"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_api.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro", "/etc/pki/tls/certs/httpd:/etc/pki/tls/certs/httpd:ro", "/etc/pki/tls/private/httpd:/etc/pki/tls/private/httpd:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}, "nova_wait_for_api_service": {"start_order": 3, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "user": "root", "net": "host", "privileged": false, "detach": false, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_wait_for_api_service.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro", "/var/lib/container-config-scripts:/container-config-scripts:z"], "environment": {"__OS_DEBUG": "true", "KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}, "nova_api_cron": {"start_order": 4, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "host", "user": "root", "privileged": false, "restart": "always", "healthcheck": {"test": "/usr/share/openstack-tripleo-common/healthcheck/cron nova"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-api:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_api_cron.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "metadata_settings": [[{"service": "HTTP", "network": "ctlplane", "type": "node"}], [{"service": "HTTP", "network": "storage", "type": "node"}], [{"service": "HTTP", "network": "storage_mgmt", "type": "node"}], [{"service": "HTTP", "network": "internal_api", "type": "node"}], [{"service": "HTTP", "network": "external", "type": "node"}]], "deploy_steps_tasks": [{"name": "Certificate generation", "when": ["step|int == 1", "enable_internal_tls"], "block": [{"name": "Create dirs for certificates and keys", "file": {"path": "{{ item }}", "state": "directory", "serole": "object_r", "setype": "cert_t", "seuser": "system_u"}, "with_items": ["/etc/pki/tls/certs/httpd", "/etc/pki/tls/private/httpd"]}, {"include_role": {"name": "linux-system-roles.certificate"}, "vars": {"certificate_requests": [{"name": "httpd-ctlplane", "dns": "{{ fqdn_ctlplane }}", "principal": "HTTP/{{ fqdn_ctlplane }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-ctlplane.crt /etc/pki/tls/certs/httpd/httpd-ctlplane.crt\ncp /etc/pki/tls/private/httpd-ctlplane.key /etc/pki/tls/private/httpd/httpd-ctlplane.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-storage", "dns": "{{ fqdn_storage }}", "principal": "HTTP/{{ fqdn_storage }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-storage.crt /etc/pki/tls/certs/httpd/httpd-storage.crt\ncp /etc/pki/tls/private/httpd-storage.key /etc/pki/tls/private/httpd/httpd-storage.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-storage_mgmt", "dns": "{{ fqdn_storage_mgmt }}", "principal": "HTTP/{{ fqdn_storage_mgmt }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-storage_mgmt.crt /etc/pki/tls/certs/httpd/httpd-storage_mgmt.crt\ncp /etc/pki/tls/private/httpd-storage_mgmt.key /etc/pki/tls/private/httpd/httpd-storage_mgmt.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-internal_api", "dns": "{{ fqdn_internal_api }}", "principal": "HTTP/{{ fqdn_internal_api }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-internal_api.crt /etc/pki/tls/certs/httpd/httpd-internal_api.crt\ncp /etc/pki/tls/private/httpd-internal_api.key /etc/pki/tls/private/httpd/httpd-internal_api.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-external", "dns": "{{ fqdn_external }}", "principal": "HTTP/{{ fqdn_external }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-external.crt /etc/pki/tls/certs/httpd/httpd-external.crt\ncp /etc/pki/tls/private/httpd-external.key /etc/pki/tls/private/httpd/httpd-external.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}]}}]}, {"name": "validate nova-api container state", "containers.podman.podman_container_info": {"name": "nova_api"}, "register": "nova_api_infos", "failed_when": ["nova_api_infos.containers.0.Healthcheck.Status is defined", "'healthy' not in nova_api_infos.containers.0.Healthcheck.Status"], "retries": 10, "delay": 30, "tags": ["opendev-validation", "opendev-validation-nova"], "when": ["container_cli == 'podman'", "not container_healthcheck_disabled", "step|int == 4"]}], "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "setype": "{{ item.setype }}", "state": "directory", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}, {"path": "/var/log/containers/httpd/nova-api", "setype": "container_file_t", "mode": "0750"}]}], "external_upgrade_tasks": [{"when": ["step|int == 1"], "tags": ["never", "system_upgrade_transfer_data", "system_upgrade_stop_services"], "block": [{"name": "Stop nova api container", "import_role": {"name": "tripleo_container_stop"}, "vars": {"tripleo_containers_to_stop": ["nova_api", "nova_api_cron"], "tripleo_delegate_to": "{{ groups['nova_api'] | difference(groups['excluded_overcloud']) }}"}}]}]}, "tuple:('role_data', 'service_names')": null, "tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "apache::default_vhost": false, "nova::keystone::authtoken::project_name": "service", "nova::keystone::authtoken::user_domain_name": "Default", "nova::keystone::authtoken::project_domain_name": "Default", "nova::keystone::authtoken::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::authtoken::www_authenticate_uri": "https://standalone.ooo.test:13000", "nova::keystone::authtoken::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::authtoken::region_name": "regionOne", "nova::keystone::authtoken::interface": "internal", "nova::keystone::authtoken::memcache_use_advanced_pool": true, "nova::api::max_limit": 1000, "nova::api::enabled": true, "nova::network::neutron::default_floating_pool": "public", "nova::api::enable_proxy_headers_parsing": true, "nova::api::api_bind_address": "%{hiera('fqdn_internal_api')}", "nova::api::service_name": "httpd", "nova::wsgi::apache_api::ssl": true, "nova::wsgi::apache_api::bind_host": "%{hiera('internal_api')}", "nova::wsgi::apache_api::servername": "%{hiera('fqdn_internal_api')}", "nova::api::instance_name_template": "instance-%08x", "nova::policy::policies": {}, "nova::api::allow_resize_to_same_host": false, "nova_enable_db_purge": true, "nova::cron::purge_shadow_tables::minute": "0", "nova::cron::purge_shadow_tables::hour": "5", "nova::cron::purge_shadow_tables::monthday": "*", "nova::cron::purge_shadow_tables::month": "*", "nova::cron::purge_shadow_tables::weekday": "*", "nova::cron::purge_shadow_tables::user": "nova", "nova::cron::purge_shadow_tables::destination": "/var/log/nova/nova-rowspurge.log", "nova::cron::purge_shadow_tables::maxdelay": "3600", "nova::cron::purge_shadow_tables::age": 14, "nova::cron::purge_shadow_tables::verbose": false, "nova::cron::purge_shadow_tables::all_cells": true, "nova_enable_db_archive": true, "nova::cron::archive_deleted_rows::minute": "1", "nova::cron::archive_deleted_rows::hour": "0", "nova::cron::archive_deleted_rows::monthday": "*", "nova::cron::archive_deleted_rows::month": "*", "nova::cron::archive_deleted_rows::weekday": "*", "nova::cron::archive_deleted_rows::max_rows": "1000", "nova::cron::archive_deleted_rows::user": "nova", "nova::cron::archive_deleted_rows::destination": "/var/log/nova/nova-rowsflush.log", "nova::cron::archive_deleted_rows::maxdelay": "3600", "nova::cron::archive_deleted_rows::until_complete": true, "nova::cron::archive_deleted_rows::purge": false, "nova::cron::archive_deleted_rows::all_cells": true, "nova::cron::archive_deleted_rows::age": 90, "nova::policy::enforce_new_defaults": false, "nova::policy::enforce_scope": false, "nova::api::osapi_compute_workers": 1, "nova::wsgi::apache_api::workers": 1}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-34-awy7ykbh3hec/8390b555-ad46-4a11-9857-d4ff578fcad0"}


created_at: 2026-06-13 07:32:13.009438
data:
  {"type": "json", "value": {"ContainerNovaConductorImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "ContainerNovaConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:13.038859
data:
  {"value": {"ContainerNovaConductorImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "ContainerNovaConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:13.120749
data:
  {"ContainerNovaImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "NovaServiceName": "conductor"}


created_at: 2026-06-13 07:32:13.349226
data:
  {"type": "json", "value": {"ContainerNovaImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1"}}


created_at: 2026-06-13 07:32:17.092050
data:
  {"healthcheck_rpc_port": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro"], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-35-evuld6ixcpl5-ContainersCommon-hlih45b3atgx/a959092a-19f6-4a5a-88f9-1e6b3a38dc02"}


created_at: 2026-06-13 07:32:17.146834
data:
  {"tuple:('role_data', 'step_config')": "include tripleo::profile::base::database::mysql::client", "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-35-evuld6ixcpl5-MySQLClient-aofv4e5nvpfo/cab56cbc-a60b-4aad-be7d-b6f12f5a0145"}


created_at: 2026-06-13 07:32:17.213536
data:
  {"tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": ""}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-35-evuld6ixcpl5-NovaBase-dc75obqkphjv/8dc0ae07-2b41-4b41-b594-bff5f401e9a8"}


created_at: 2026-06-13 07:32:17.242891
data:
  {"tuple:('role_data', 'config_settings')": {"nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo"}, "tuple:('role_data', 'service_config_settings', 'mysql')": {"nova::db::mysql::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql::user": "nova", "nova::db::mysql::host": "%", "nova::db::mysql::dbname": "nova"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-35-evuld6ixcpl5-NovaDBClient-frgt32imt2zq/e1f28154-21c9-4457-a898-01baf5e89030"}


created_at: 2026-06-13 07:32:17.357649
data:
  {"tuple:('docker_config', 'step_2')": {"nova_conductor_init_log": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "cmd_extra_args": "", "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "setype": "{{ item.setype }}", "state": "directory", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}]}], "config_settings": null, "volumes": ["/var/log/containers/nova:/var/log/nova"], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-35-evuld6ixcpl5-NovaLogging-ss2kvgzfxvaw/5a5d902d-eed4-470b-af1f-5b81115c0e34"}


created_at: 2026-06-13 07:32:17.394955
data:
  {"tuple:('role_data', 'config_settings')": {"nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo"}, "tuple:('role_data', 'service_config_settings', 'mysql')": {"nova::db::mysql_api::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql_api::user": "nova_api", "nova::db::mysql_api::host": "%", "nova::db::mysql_api::dbname": "nova_api"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-35-evuld6ixcpl5-NovaApiDBClient-uka2mgk5uvwe/17c92253-e1e4-4331-b782-fb93d50439cb"}


created_at: 2026-06-13 07:32:17.406964
data:
  {"tuple:('role_data', 'step_config')": null, "tuple:('role_data', 'container_config_scripts')": null, "tuple:('role_data', 'docker_config')": {"step_2": {"nova_conductor_init_log": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "step_3": {"nova_db_sync": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "cap_add": ["AUDIT_WRITE"], "start_order": 3, "net": "host", "detach": false, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova", "/var/lib/kolla/config_files/nova_conductor_db_sync.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "user": "root", "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS", "TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "step_4": {"nova_conductor": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "net": "host", "privileged": false, "restart": "always", "healthcheck": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova", "/var/lib/kolla/config_files/nova_conductor.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "tuple:('role_data', 'service_name')": "nova_conductor", "role_data": {"service_name": "nova_conductor", "monitoring_subscription": "overcloud-nova-conductor", "config_settings": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::conductor::workers": 1}, "service_config_settings": {"rabbitmq": {"get_attr": ["NovaBase", "role_data", "service_config_settings"], "rabbitmq": null}, "mysql": {"nova::db::mysql_api::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql_api::user": "nova_api", "nova::db::mysql_api::host": "%", "nova::db::mysql_api::dbname": "nova_api", "nova::db::mysql::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql::user": "nova", "nova::db::mysql::host": "%", "nova::db::mysql::dbname": "nova"}, "rsyslog": {"tripleo_logging_sources_nova_conductor": [{"tag": "openstack.nova.conductor", "file": "/var/log/containers/nova/nova-conductor.log"}]}}, "puppet_config": {"config_volume": "nova", "puppet_tags": "nova_config", "step_config": "include tripleo::profile::base::nova::conductor\ninclude tripleo::profile::base::database::mysql::client", "config_image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}, "kolla_config": {"/var/lib/kolla/config_files/nova_conductor.json": {"command": "/usr/bin/nova-conductor ", "config_files": [{"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}, "/var/lib/kolla/config_files/nova_conductor_db_sync.json": {"command": "/usr/bin/bootstrap_host_exec nova_conductor su nova -s /bin/bash -c '/usr/bin/nova-manage db sync '", "config_files": [{"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}}, "docker_config": {"step_2": {"nova_conductor_init_log": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "step_3": {"nova_db_sync": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "cap_add": ["AUDIT_WRITE"], "start_order": 3, "net": "host", "detach": false, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova", "/var/lib/kolla/config_files/nova_conductor_db_sync.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "user": "root", "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS", "TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "step_4": {"nova_conductor": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-conductor:17.1", "net": "host", "privileged": false, "restart": "always", "healthcheck": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova", "/var/lib/kolla/config_files/nova_conductor.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "deploy_steps_tasks": [{"name": "validate nova-conductor container state", "containers.podman.podman_container_info": {"name": "nova_conductor"}, "register": "nova_conductor_infos", "failed_when": ["nova_conductor_infos.containers.0.Healthcheck.Status is defined", "'healthy' not in nova_conductor_infos.containers.0.Healthcheck.Status"], "retries": 10, "delay": 30, "tags": ["opendev-validation", "opendev-validation-nova"], "when": ["container_cli == 'podman'", "not container_healthcheck_disabled", "step|int == 5"]}], "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "setype": "{{ item.setype }}", "state": "directory", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}]}, {"name": "enable virt_sandbox_use_netlink for healthcheck", "seboolean": {"name": "virt_sandbox_use_netlink", "persistent": true, "state": true}, "when": ["ansible_facts.selinux is defined", "ansible_facts.selinux.status == \"enabled\""]}], "external_upgrade_tasks": [{"when": "step|int == 1", "block": [{"name": "Set facts for running online DB migrations", "set_fact": {"is_additional_cell": false}, "tags": ["online_upgrade", "online_upgrade_nova"]}, {"name": "Online data migration for Nova", "when": "not is_additional_cell|bool", "command": "{{ container_cli }} exec nova_conductor nova-manage db online_data_migrations", "delegate_to": "{{ (groups['nova_conductor'] | difference(groups['excluded_overcloud']))[0] }}", "become": true, "tags": ["online_upgrade", "online_upgrade_nova"]}]}, {"when": ["step|int == 1"], "tags": ["never", "system_upgrade_transfer_data", "system_upgrade_stop_services"], "block": [{"name": "Stop nova conductor container", "import_role": {"name": "tripleo_container_stop"}, "vars": {"tripleo_containers_to_stop": ["nova_conductor"], "tripleo_delegate_to": "{{ groups['nova_conductor'] | difference(groups['excluded_overcloud']) }}"}}]}], "external_update_tasks": [{"when": "step|int == 1", "block": [{"name": "Set facts for running online DB migrations", "set_fact": {"is_additional_cell": false}, "tags": ["online_upgrade", "online_upgrade_nova"]}, {"name": "Online data migration for Nova", "when": "not is_additional_cell|bool", "command": "{{ container_cli }} exec nova_conductor nova-manage db online_data_migrations", "delegate_to": "{{ (groups['nova_conductor'] | difference(groups['excluded_overcloud']))[0] }}", "become": true, "tags": ["online_upgrade", "online_upgrade_nova"]}]}]}, "tuple:('role_data', 'service_names')": null, "tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::conductor::workers": 1}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-35-evuld6ixcpl5/86469b9c-c12d-48ee-93f3-cde897a41d86"}


created_at: 2026-06-13 07:32:20.797414
data:
  {"type": "json", "value": {"ContainerNovaMetadataImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "ContainerNovaMetadataConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:22.958885
data:
  {"type": "json", "value": {"ContainerNovaMetadataImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:26.909290
data:
  {"volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro"], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-36-fr3s5hppzdct-ContainersCommon-5o7tx6pem5vc/15ddd8ba-26d9-41d3-b418-3563e5cff46f"}


created_at: 2026-06-13 07:32:26.966866
data:
  {"tuple:('role_data', 'config_settings')": {"nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo"}, "tuple:('role_data', 'service_config_settings', 'mysql')": {"nova::db::mysql::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql::user": "nova", "nova::db::mysql::host": "%", "nova::db::mysql::dbname": "nova"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-36-fr3s5hppzdct-NovaDBClient-gigdls5ykdlm/76a3e58a-aa2f-4c3a-b1a7-550a224e22bb"}


created_at: 2026-06-13 07:32:27.031253
data:
  {"tuple:('role_data', 'deploy_steps_tasks')": [{"name": "Certificate generation", "when": ["step|int == 1", "enable_internal_tls"], "block": [{"name": "Create dirs for certificates and keys", "file": {"path": "{{ item }}", "state": "directory", "serole": "object_r", "setype": "cert_t", "seuser": "system_u"}, "with_items": ["/etc/pki/tls/certs/httpd", "/etc/pki/tls/private/httpd"]}, {"include_role": {"name": "linux-system-roles.certificate"}, "vars": {"certificate_requests": [{"name": "httpd-ctlplane", "dns": "{{ fqdn_ctlplane }}", "principal": "HTTP/{{ fqdn_ctlplane }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-ctlplane.crt /etc/pki/tls/certs/httpd/httpd-ctlplane.crt\ncp /etc/pki/tls/private/httpd-ctlplane.key /etc/pki/tls/private/httpd/httpd-ctlplane.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-storage", "dns": "{{ fqdn_storage }}", "principal": "HTTP/{{ fqdn_storage }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-storage.crt /etc/pki/tls/certs/httpd/httpd-storage.crt\ncp /etc/pki/tls/private/httpd-storage.key /etc/pki/tls/private/httpd/httpd-storage.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-storage_mgmt", "dns": "{{ fqdn_storage_mgmt }}", "principal": "HTTP/{{ fqdn_storage_mgmt }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-storage_mgmt.crt /etc/pki/tls/certs/httpd/httpd-storage_mgmt.crt\ncp /etc/pki/tls/private/httpd-storage_mgmt.key /etc/pki/tls/private/httpd/httpd-storage_mgmt.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-internal_api", "dns": "{{ fqdn_internal_api }}", "principal": "HTTP/{{ fqdn_internal_api }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-internal_api.crt /etc/pki/tls/certs/httpd/httpd-internal_api.crt\ncp /etc/pki/tls/private/httpd-internal_api.key /etc/pki/tls/private/httpd/httpd-internal_api.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-external", "dns": "{{ fqdn_external }}", "principal": "HTTP/{{ fqdn_external }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-external.crt /etc/pki/tls/certs/httpd/httpd-external.crt\ncp /etc/pki/tls/private/httpd-external.key /etc/pki/tls/private/httpd/httpd-external.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}]}}]}], "tuple:('role_data', 'metadata_settings')": [[{"service": "HTTP", "network": "ctlplane", "type": "node"}], [{"service": "HTTP", "network": "storage", "type": "node"}], [{"service": "HTTP", "network": "storage_mgmt", "type": "node"}], [{"service": "HTTP", "network": "internal_api", "type": "node"}], [{"service": "HTTP", "network": "external", "type": "node"}]], "tuple:('role_data', 'config_settings')": {"apache::ip": "%{hiera('internal_api')}", "apache::default_vhost": false, "apache::trace_enable": "Off", "apache::server_signature": "Off", "apache::server_tokens": "Prod", "apache::timeout": 90, "apache::default_mods": false, "apache::mod::prefork::maxrequestworkers": 100, "apache::mod::prefork::serverlimit": 100, "apache::mod::remoteip::proxy_ips": ["172.17.0.0/24"], "apache::mod::alias::icons_options": "None", "apache::mod::ssl::ssl_ca": "/etc/ipa/ca.crt", "apache::mod::ssl::ssl_protocol": ["all", "-SSLv2", "-SSLv3", "-TLSv1"], "apache_certificates_specs": {"httpd-ctlplane": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-ctlplane.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-ctlplane.key"}, "httpd-storage": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-storage.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-storage.key"}, "httpd-storage_mgmt": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-storage_mgmt.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-storage_mgmt.key"}, "httpd-internal_api": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-internal_api.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-internal_api.key"}, "httpd-external": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-external.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-external.key"}}}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-36-fr3s5hppzdct-ApacheServiceBase-zrjxznm6sqrj/33a01e42-b060-43ef-b8e5-0537e7d26d2a"}


created_at: 2026-06-13 07:32:27.060172
data:
  {"tuple:('role_data', 'config_settings')": {"nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo"}, "tuple:('role_data', 'service_config_settings', 'mysql')": {"nova::db::mysql_api::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql_api::user": "nova_api", "nova::db::mysql_api::host": "%", "nova::db::mysql_api::dbname": "nova_api"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-36-fr3s5hppzdct-NovaApiDBClient-dbpvs4vyi4h4/2ed574a2-564f-4657-b468-fdbde77c4aa7"}


created_at: 2026-06-13 07:32:27.114898
data:
  {"tuple:('role_data', 'step_config')": "include tripleo::profile::base::database::mysql::client", "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-36-fr3s5hppzdct-MySQLClient-fm6esh62rgbc/be207385-d62a-4659-841f-ff1f9947dc7f"}


created_at: 2026-06-13 07:32:27.182633
data:
  {"tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": ""}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-36-fr3s5hppzdct-NovaBase-ut3cqo6bjcut/917f874c-1aa3-4df2-b6e0-2192d5d40a72"}


created_at: 2026-06-13 07:32:27.186535
data:
  {"value": {"ContainerNovaMetadataImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "ContainerNovaMetadataConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:27.275099
data:
  {"volumes": ["/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-metadata:/var/log/httpd:z"], "tuple:('docker_config', 'step_2')": {"nova_metadata_init_logs": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-metadata:/var/log/httpd:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"]}}, "config_settings": null, "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "state": "directory", "setype": "{{ item.setype }}", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}, {"path": "/var/log/containers/httpd/nova-metadata", "setype": "container_file_t", "mode": "0750"}]}], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-36-fr3s5hppzdct-NovaMetadataLogging-felcsdq5rgzn/3b0c8fcc-bc62-4e97-b8a9-822127df2fd0"}


created_at: 2026-06-13 07:32:27.287313
data:
  {"tuple:('role_data', 'step_config')": null, "tuple:('role_data', 'container_config_scripts')": null, "tuple:('role_data', 'docker_config')": {"step_2": {"nova_metadata_init_logs": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-metadata:/var/log/httpd:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"]}}, "step_4": {"nova_metadata": {"start_order": 2, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "host", "user": "root", "restart": "always", "healthcheck": {"test": "/openstack/healthcheck"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-metadata:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_metadata.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova_metadata:/var/lib/kolla/config_files/src:ro", "/etc/pki/tls/certs/httpd:/etc/pki/tls/certs/httpd:ro", "/etc/pki/tls/private/httpd:/etc/pki/tls/private/httpd:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "tuple:('role_data', 'service_name')": "nova_metadata", "role_data": {"service_name": "nova_metadata", "firewall_rules": {"139 nova_metadata": {"dport": [8775]}}, "firewall_frontend_rules": {"100 nova_metadatahaproxy_frontend": {"dport": [8775]}}, "firewall_ssl_frontend_rules": {"100 nova_metadatahaproxy_frontend_ssl": {"dport": [13775]}}, "monitoring_subscription": "overcloud-nova-metadata", "config_settings": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "apache::ip": "%{hiera('internal_api')}", "apache::default_vhost": false, "apache::trace_enable": "Off", "apache::server_signature": "Off", "apache::server_tokens": "Prod", "apache::timeout": 90, "apache::default_mods": false, "apache::mod::prefork::maxrequestworkers": 100, "apache::mod::prefork::serverlimit": 100, "apache::mod::remoteip::proxy_ips": ["172.17.0.0/24"], "apache::mod::alias::icons_options": "None", "apache::mod::ssl::ssl_ca": "/etc/ipa/ca.crt", "apache::mod::ssl::ssl_protocol": ["all", "-SSLv2", "-SSLv3", "-TLSv1"], "apache_certificates_specs": {"httpd-ctlplane": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-ctlplane.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-ctlplane.key"}, "httpd-storage": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-storage.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-storage.key"}, "httpd-storage_mgmt": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-storage_mgmt.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-storage_mgmt.key"}, "httpd-internal_api": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-internal_api.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-internal_api.key"}, "httpd-external": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-external.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-external.key"}}, "nova::keystone::authtoken::project_name": "service", "nova::keystone::authtoken::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::authtoken::www_authenticate_uri": "https://standalone.ooo.test:13000", "nova::keystone::authtoken::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::authtoken::region_name": "regionOne", "nova::keystone::authtoken::interface": "internal", "nova::keystone::authtoken::memcache_use_advanced_pool": true, "nova::wsgi::apache_metadata::api_port": "8775", "nova::wsgi::apache_metadata::ssl": true, "nova::metadata::local_metadata_per_cell": false, "nova::wsgi::apache_metadata::bind_host": "%{hiera('internal_api')}", "nova::wsgi::apache_metadata::servername": "%{hiera('fqdn_internal_api')}", "nova::wsgi::apache_metadata::workers": 1, "nova::metadata::neutron_metadata_proxy_shared_secret": "bxeW2RrqpEnHixt0KeySTY5OI"}, "service_config_settings": {"rabbitmq": {"get_attr": ["NovaBase", "role_data", "service_config_settings"], "rabbitmq": null}, "mysql": {"nova::db::mysql_api::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql_api::user": "nova_api", "nova::db::mysql_api::host": "%", "nova::db::mysql_api::dbname": "nova_api", "nova::db::mysql::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql::user": "nova", "nova::db::mysql::host": "%", "nova::db::mysql::dbname": "nova"}, "rsyslog": {"tripleo_logging_sources_nova_metadata": [{"tag": "openstack.nova.api.metadata", "file": "/var/log/containers/nova/nova-metadata-api.log"}]}}, "puppet_config": {"config_volume": "nova_metadata", "puppet_tags": "nova_config,nova_api_paste_ini", "step_config": "include tripleo::profile::base::nova::metadata\ninclude tripleo::profile::base::database::mysql::client", "config_image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}, "kolla_config": {"/var/lib/kolla/config_files/nova_metadata.json": {"command": "/usr/sbin/httpd -DFOREGROUND", "config_files": [{"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.d", "dest": "/etc/httpd/conf.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/etc/httpd/conf.modules.d", "dest": "/etc/httpd/conf.modules.d", "merge": false, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}}, "docker_config": {"step_2": {"nova_metadata_init_logs": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-metadata:/var/log/httpd:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"]}}, "step_4": {"nova_metadata": {"start_order": 2, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1", "net": "host", "user": "root", "restart": "always", "healthcheck": {"test": "/openstack/healthcheck"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova:z", "/var/log/containers/httpd/nova-metadata:/var/log/httpd:z", "/var/lib/kolla/config_files/nova_metadata.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova_metadata:/var/lib/kolla/config_files/src:ro", "/etc/pki/tls/certs/httpd:/etc/pki/tls/certs/httpd:ro", "/etc/pki/tls/private/httpd:/etc/pki/tls/private/httpd:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "deploy_steps_tasks": [{"name": "Certificate generation", "when": ["step|int == 1", "enable_internal_tls"], "block": [{"name": "Create dirs for certificates and keys", "file": {"path": "{{ item }}", "state": "directory", "serole": "object_r", "setype": "cert_t", "seuser": "system_u"}, "with_items": ["/etc/pki/tls/certs/httpd", "/etc/pki/tls/private/httpd"]}, {"include_role": {"name": "linux-system-roles.certificate"}, "vars": {"certificate_requests": [{"name": "httpd-ctlplane", "dns": "{{ fqdn_ctlplane }}", "principal": "HTTP/{{ fqdn_ctlplane }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-ctlplane.crt /etc/pki/tls/certs/httpd/httpd-ctlplane.crt\ncp /etc/pki/tls/private/httpd-ctlplane.key /etc/pki/tls/private/httpd/httpd-ctlplane.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-storage", "dns": "{{ fqdn_storage }}", "principal": "HTTP/{{ fqdn_storage }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-storage.crt /etc/pki/tls/certs/httpd/httpd-storage.crt\ncp /etc/pki/tls/private/httpd-storage.key /etc/pki/tls/private/httpd/httpd-storage.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-storage_mgmt", "dns": "{{ fqdn_storage_mgmt }}", "principal": "HTTP/{{ fqdn_storage_mgmt }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-storage_mgmt.crt /etc/pki/tls/certs/httpd/httpd-storage_mgmt.crt\ncp /etc/pki/tls/private/httpd-storage_mgmt.key /etc/pki/tls/private/httpd/httpd-storage_mgmt.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-internal_api", "dns": "{{ fqdn_internal_api }}", "principal": "HTTP/{{ fqdn_internal_api }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-internal_api.crt /etc/pki/tls/certs/httpd/httpd-internal_api.crt\ncp /etc/pki/tls/private/httpd-internal_api.key /etc/pki/tls/private/httpd/httpd-internal_api.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}, {"name": "httpd-external", "dns": "{{ fqdn_external }}", "principal": "HTTP/{{ fqdn_external }}@{{ idm_realm }}", "run_after": "cp /etc/pki/tls/certs/httpd-external.crt /etc/pki/tls/certs/httpd/httpd-external.crt\ncp /etc/pki/tls/private/httpd-external.key /etc/pki/tls/private/httpd/httpd-external.key\npkill -USR1 httpd\n", "key_size": "2048", "ca": "ipa"}]}}]}, {"name": "validate nova-metadata container state", "containers.podman.podman_container_info": {"name": "nova_metadata"}, "register": "nova_metadata_infos", "failed_when": ["nova_metadata_infos.containers.0.Healthcheck.Status is defined", "'healthy' not in nova_metadata_infos.containers.0.Healthcheck.Status"], "retries": 10, "delay": 30, "tags": ["opendev-validation", "opendev-validation-nova"], "when": ["container_cli == 'podman'", "not container_healthcheck_disabled", "step|int == 5"]}], "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "state": "directory", "setype": "{{ item.setype }}", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}, {"path": "/var/log/containers/httpd/nova-metadata", "setype": "container_file_t", "mode": "0750"}]}], "metadata_settings": [[{"service": "HTTP", "network": "ctlplane", "type": "node"}], [{"service": "HTTP", "network": "storage", "type": "node"}], [{"service": "HTTP", "network": "storage_mgmt", "type": "node"}], [{"service": "HTTP", "network": "internal_api", "type": "node"}], [{"service": "HTTP", "network": "external", "type": "node"}]], "external_upgrade_tasks": [{"when": ["step|int == 1"], "tags": ["never", "system_upgrade_transfer_data", "system_upgrade_stop_services"], "block": [{"name": "Stop nova metadata container", "import_role": {"name": "tripleo_container_stop"}, "vars": {"tripleo_containers_to_stop": ["nova_metadata"], "tripleo_delegate_to": "{{ groups['nova_metadata'] | difference(groups['excluded_overcloud']) }}"}}]}]}, "tuple:('role_data', 'service_names')": null, "tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "apache::ip": "%{hiera('internal_api')}", "apache::default_vhost": false, "apache::trace_enable": "Off", "apache::server_signature": "Off", "apache::server_tokens": "Prod", "apache::timeout": 90, "apache::default_mods": false, "apache::mod::prefork::maxrequestworkers": 100, "apache::mod::prefork::serverlimit": 100, "apache::mod::remoteip::proxy_ips": ["172.17.0.0/24"], "apache::mod::alias::icons_options": "None", "apache::mod::ssl::ssl_ca": "/etc/ipa/ca.crt", "apache::mod::ssl::ssl_protocol": ["all", "-SSLv2", "-SSLv3", "-TLSv1"], "apache_certificates_specs": {"httpd-ctlplane": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-ctlplane.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-ctlplane.key"}, "httpd-storage": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-storage.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-storage.key"}, "httpd-storage_mgmt": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-storage_mgmt.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-storage_mgmt.key"}, "httpd-internal_api": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-internal_api.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-internal_api.key"}, "httpd-external": {"service_certificate": "/etc/pki/tls/certs/httpd/httpd-external.crt", "service_key": "/etc/pki/tls/private/httpd/httpd-external.key"}}, "nova::keystone::authtoken::project_name": "service", "nova::keystone::authtoken::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::authtoken::www_authenticate_uri": "https://standalone.ooo.test:13000", "nova::keystone::authtoken::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::authtoken::region_name": "regionOne", "nova::keystone::authtoken::interface": "internal", "nova::keystone::authtoken::memcache_use_advanced_pool": true, "nova::wsgi::apache_metadata::api_port": "8775", "nova::wsgi::apache_metadata::ssl": true, "nova::metadata::local_metadata_per_cell": false, "nova::wsgi::apache_metadata::bind_host": "%{hiera('internal_api')}", "nova::wsgi::apache_metadata::servername": "%{hiera('fqdn_internal_api')}", "nova::wsgi::apache_metadata::workers": 1, "nova::metadata::neutron_metadata_proxy_shared_secret": "bxeW2RrqpEnHixt0KeySTY5OI"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-36-fr3s5hppzdct/f615b604-9bb4-44dd-8b0c-edf8898d0014"}


created_at: 2026-06-13 07:32:30.667390
data:
  {"type": "json", "value": {"ContainerNovaSchedulerImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-scheduler:17.1", "ContainerNovaConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:30.698058
data:
  {"value": {"ContainerNovaSchedulerImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-scheduler:17.1", "ContainerNovaConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}}


created_at: 2026-06-13 07:32:30.772240
data:
  {"ContainerNovaImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-scheduler:17.1", "NovaServiceName": "scheduler"}


created_at: 2026-06-13 07:32:31.894748
data:
  {"type": "json", "value": {"ContainerNovaImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-scheduler:17.1"}}


created_at: 2026-06-13 07:32:34.730940
data:
  {"healthcheck_rpc_port": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro"], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-37-nlulp4y6hxng-ContainersCommon-dybgoyuxb4ij/377bdff0-579a-403a-b9e4-4e11591dd135"}


created_at: 2026-06-13 07:32:34.786772
data:
  {"tuple:('role_data', 'step_config')": "include tripleo::profile::base::database::mysql::client", "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-37-nlulp4y6hxng-MySQLClient-hc4dj5n6yfx4/fae91390-a94d-47be-8680-584d32e6d745"}


created_at: 2026-06-13 07:32:34.853375
data:
  {"tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": ""}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-37-nlulp4y6hxng-NovaBase-ackaomtelhvq/e145f53a-e550-4074-ae4c-1d50f6d27344"}


created_at: 2026-06-13 07:32:34.887980
data:
  {"tuple:('role_data', 'config_settings')": {"nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo"}, "tuple:('role_data', 'service_config_settings', 'mysql')": {"nova::db::mysql::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql::user": "nova", "nova::db::mysql::host": "%", "nova::db::mysql::dbname": "nova"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-37-nlulp4y6hxng-NovaDBClient-z2htzb7ug6eb/d7127809-998a-4750-ac3b-c1ecd1ba4376"}


created_at: 2026-06-13 07:32:35.036776
data:
  {"cmd_extra_args": "", "volumes": ["/var/log/containers/nova:/var/log/nova"], "config_settings": null, "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "setype": "{{ item.setype }}", "state": "directory", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}]}], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-37-nlulp4y6hxng-NovaLogging-fcoyahohpqnl/3b036357-224a-4af8-9369-feb53f8f8e6c"}


created_at: 2026-06-13 07:32:35.094460
data:
  {"tuple:('role_data', 'config_settings')": {"nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo"}, "tuple:('role_data', 'service_config_settings', 'mysql')": {"nova::db::mysql_api::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql_api::user": "nova_api", "nova::db::mysql_api::host": "%", "nova::db::mysql_api::dbname": "nova_api"}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-37-nlulp4y6hxng-NovaApiDBClient-mxq6id2sq7au/36d0ae15-22ee-4144-bf31-a98c8d719299"}


created_at: 2026-06-13 07:32:35.106051
data:
  {"tuple:('role_data', 'step_config')": null, "tuple:('role_data', 'container_config_scripts')": null, "tuple:('role_data', 'docker_config')": {"step_4": {"nova_scheduler": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-scheduler:17.1", "net": "host", "privileged": false, "restart": "always", "healthcheck": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova", "/var/lib/kolla/config_files/nova_scheduler.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "tuple:('role_data', 'service_name')": "nova_scheduler", "role_data": {"service_name": "nova_scheduler", "monitoring_subscription": "overcloud-nova-scheduler", "config_settings": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::scheduler::filter::scheduler_available_filters": ["nova.scheduler.filters.all_filters", "nova.scheduler.filters.pci_passthrough_filter.PciPassthroughFilter"], "nova::scheduler::filter::scheduler_enabled_filters": ["AvailabilityZoneFilter", "ComputeFilter", "ComputeCapabilitiesFilter", "ImagePropertiesFilter", "ServerGroupAntiAffinityFilter", "ServerGroupAffinityFilter", "PciPassthroughFilter"], "nova::scheduler::filter::scheduler_max_attempts": 3, "nova::scheduler::filter::scheduler_host_subset_size": 1, "nova::scheduler::filter::shuffle_best_same_weighed_hosts": false, "nova::scheduler::discover_hosts_in_cells_interval": -1, "nova::scheduler::query_placement_for_image_type_support": true, "nova::scheduler::limit_tenants_to_placement_aggregate": false, "nova::scheduler::placement_aggregate_required_for_tenants": false, "nova::scheduler::enable_isolated_aggregate_filtering": false, "nova::scheduler::query_placement_for_availability_zone": false, "nova::scheduler::query_placement_for_routed_network_aggregates": false, "nova::scheduler::workers": 1}, "service_config_settings": {"rabbitmq": {"get_attr": ["NovaBase", "role_data", "service_config_settings"], "rabbitmq": null}, "mysql": {"nova::db::mysql_api::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql_api::user": "nova_api", "nova::db::mysql_api::host": "%", "nova::db::mysql_api::dbname": "nova_api", "nova::db::mysql::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::db::mysql::user": "nova", "nova::db::mysql::host": "%", "nova::db::mysql::dbname": "nova"}, "rsyslog": {"tripleo_logging_sources_nova_scheduler": [{"tag": "openstack.nova.scheduler", "file": "/var/log/containers/nova/nova-scheduler.log"}]}}, "puppet_config": {"config_volume": "nova", "puppet_tags": "nova_config", "step_config": "include tripleo::profile::base::nova::scheduler\ninclude tripleo::profile::base::database::mysql::client", "config_image": "registry.redhat.io/rhosp-rhel9/openstack-nova-api:17.1"}, "kolla_config": {"/var/lib/kolla/config_files/nova_scheduler.json": {"command": "/usr/bin/nova-scheduler ", "config_files": [{"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}}, "docker_config": {"step_4": {"nova_scheduler": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-scheduler:17.1", "net": "host", "privileged": false, "restart": "always", "healthcheck": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova", "/var/lib/kolla/config_files/nova_scheduler.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS"}}}}, "deploy_steps_tasks": [{"name": "validate nova-scheduler container state", "containers.podman.podman_container_info": {"name": "nova_scheduler"}, "register": "nova_scheduler_infos", "failed_when": ["nova_scheduler_infos.containers.0.Healthcheck.Status is defined", "'healthy' not in nova_scheduler_infos.containers.0.Healthcheck.Status"], "retries": 10, "delay": 30, "tags": ["opendev-validation", "opendev-validation-nova"], "when": ["container_cli == 'podman'", "not container_healthcheck_disabled", "step|int == 5"]}], "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "setype": "{{ item.setype }}", "state": "directory", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}]}, {"name": "enable virt_sandbox_use_netlink for healthcheck", "seboolean": {"name": "virt_sandbox_use_netlink", "persistent": true, "state": true}, "when": ["ansible_facts.selinux is defined", "ansible_facts.selinux.status == \"enabled\""]}], "external_upgrade_tasks": [{"when": ["step|int == 1"], "tags": ["never", "system_upgrade_transfer_data", "system_upgrade_stop_services"], "block": [{"name": "Stop nova scheduler container", "import_role": {"name": "tripleo_container_stop"}, "vars": {"tripleo_containers_to_stop": ["nova_scheduler"], "tripleo_delegate_to": "{{ groups['nova_scheduler'] | difference(groups['excluded_overcloud']) }}"}}]}]}, "tuple:('role_data', 'service_names')": null, "tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::api_database_connection": "mysql+pymysql://nova_api:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova_api?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::database_connection": "mysql+pymysql://nova:ksmPDM6JveiqLvoFg56EAxny1@overcloud.internalapi.ooo.test/nova?read_default_file=/etc/my.cnf.d/tripleo.cnf&read_default_group=tripleo", "nova::scheduler::filter::scheduler_available_filters": ["nova.scheduler.filters.all_filters", "nova.scheduler.filters.pci_passthrough_filter.PciPassthroughFilter"], "nova::scheduler::filter::scheduler_enabled_filters": ["AvailabilityZoneFilter", "ComputeFilter", "ComputeCapabilitiesFilter", "ImagePropertiesFilter", "ServerGroupAntiAffinityFilter", "ServerGroupAffinityFilter", "PciPassthroughFilter"], "nova::scheduler::filter::scheduler_max_attempts": 3, "nova::scheduler::filter::scheduler_host_subset_size": 1, "nova::scheduler::filter::shuffle_best_same_weighed_hosts": false, "nova::scheduler::discover_hosts_in_cells_interval": -1, "nova::scheduler::query_placement_for_image_type_support": true, "nova::scheduler::limit_tenants_to_placement_aggregate": false, "nova::scheduler::placement_aggregate_required_for_tenants": false, "nova::scheduler::enable_isolated_aggregate_filtering": false, "nova::scheduler::query_placement_for_availability_zone": false, "nova::scheduler::query_placement_for_routed_network_aggregates": false, "nova::scheduler::workers": 1}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-37-nlulp4y6hxng/9686bfe0-b02e-4f7a-8c62-80abeeb32ce8"}


created_at: 2026-06-13 07:32:38.501822
data:
  {"type": "json", "value": {"nova::compute::vcpu_pin_set": [], "nova::compute::cpu_shared_set": [], "nova::compute::cpu_dedicated_set": [], "nova::compute::reserved_host_memory": 512, "nova::compute::reserved_huge_pages": [], "nova::compute::neutron_physnets_numa_nodes_mapping": {}, "nova::compute::neutron_tunnel_numa_nodes": [], "nova::compute::resume_guests_state_on_host_boot": false, "nova::compute::libvirt::rx_queue_size": 512, "nova::compute::libvirt::tx_queue_size": 512, "nova::compute::libvirt::file_backed_memory": 0, "nova::compute::libvirt::volume_use_multipath": false, "nova::compute::libvirt::hw_machine_type": "x86_64=pc-q35-rhel9.0.0", "nova::compute::mkisofs_cmd": "mkisofs", "compute_enable_ksm": false, "nova::compute::rbd::libvirt_images_rbd_pool": "vms", "tripleo::profile::base::nova::compute::nova_nfs_enabled": false, "nfs_backend_enable": false, "nfs_share": "", "nfs_options": "context=system_u:object_r:nfs_t:s0", "nfs_vers": "4.2", "nova::compute::libvirt::num_pcie_ports": 16, "nova::compute::libvirt::mem_stats_period_seconds": 10, "nova::compute::rbd::ephemeral_storage": false, "resume_guests_state_on_host_boot": false, "nova::cpu_allocation_ratio": 0.0, "nova::ram_allocation_ratio": 1.0, "nova::disk_allocation_ratio": 0.0, "nova::compute::max_disk_devices_to_attach": -1, "nova::compute::libvirt::pmem_namespaces": "", "nova_pmem_namespaces": "", "nova::compute::image_cache::remove_unused_original_minimum_age_seconds": 86400, "nova::compute::libvirt::swtpm_enabled": false, "nova::compute::vgpu::vgpu_types_device_addresses_mapping": {}, "compute_disable_irqbalance": false, "nova::compute::libvirt::cpu_mode": "host-model", "nova::compute::libvirt::cpu_models": [], "nova::compute::libvirt::cpu_model_extra_flags": "", "nova_compute_opt_volumes": [], "nova_compute_startup_delay": 0, "nova_compute_opt_env_vars": {}, "nova::workarounds::never_download_image_if_on_rbd": false, "nova::compute::image_type_exclude_list": [], "nova_permit_post_copy": true, "nova_permit_auto_converge": true, "nova_libvirt_max_queues": 0, "nova_glance_enable_rbd_download": false, "nova_glance_rbd_download_multistore_id": "", "nova::compute::force_raw_images": true, "nova::compute::use_cow_images": true, "nova::compute::libvirt::preallocate_images": "none", "nova::compute::image_cache::manager_interval": 2400, "nova::compute::image_cache::remove_unused_base_images": true, "nova::compute::image_cache::remove_unused_resized_minimum_age_seconds": 3600, "nova::compute::image_cache::precache_concurrency": 1, "nova::compute::provider::custom_inventories": {}, "nova::compute::rbd::libvirt_images_rbd_glance_copy_poll_interval": 15, "nova::compute::rbd::libvirt_images_rbd_glance_copy_timeout": 600, "ContainerNovaComputeImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "ContainerNovaLibvirtConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-libvirt:17.1"}}


created_at: 2026-06-13 07:32:38.534237
data:
  {"value": {"nova::compute::vcpu_pin_set": [], "nova::compute::cpu_shared_set": [], "nova::compute::cpu_dedicated_set": [], "nova::compute::reserved_host_memory": 512, "nova::compute::reserved_huge_pages": [], "nova::compute::neutron_physnets_numa_nodes_mapping": {}, "nova::compute::neutron_tunnel_numa_nodes": [], "nova::compute::resume_guests_state_on_host_boot": false, "nova::compute::libvirt::rx_queue_size": 512, "nova::compute::libvirt::tx_queue_size": 512, "nova::compute::libvirt::file_backed_memory": 0, "nova::compute::libvirt::volume_use_multipath": false, "nova::compute::libvirt::hw_machine_type": "x86_64=pc-q35-rhel9.0.0", "nova::compute::mkisofs_cmd": "mkisofs", "compute_enable_ksm": false, "nova::compute::rbd::libvirt_images_rbd_pool": "vms", "tripleo::profile::base::nova::compute::nova_nfs_enabled": false, "nfs_backend_enable": false, "nfs_share": "", "nfs_options": "context=system_u:object_r:nfs_t:s0", "nfs_vers": "4.2", "nova::compute::libvirt::num_pcie_ports": 16, "nova::compute::libvirt::mem_stats_period_seconds": 10, "nova::compute::rbd::ephemeral_storage": false, "resume_guests_state_on_host_boot": false, "nova::cpu_allocation_ratio": 0.0, "nova::ram_allocation_ratio": 1.0, "nova::disk_allocation_ratio": 0.0, "nova::compute::max_disk_devices_to_attach": -1, "nova::compute::libvirt::pmem_namespaces": "", "nova_pmem_namespaces": "", "nova::compute::image_cache::remove_unused_original_minimum_age_seconds": 86400, "nova::compute::libvirt::swtpm_enabled": false, "nova::compute::vgpu::vgpu_types_device_addresses_mapping": {}, "compute_disable_irqbalance": false, "nova::compute::libvirt::cpu_mode": "host-model", "nova::compute::libvirt::cpu_models": [], "nova::compute::libvirt::cpu_model_extra_flags": "", "nova_compute_opt_volumes": [], "nova_compute_startup_delay": 0, "nova_compute_opt_env_vars": {}, "nova::workarounds::never_download_image_if_on_rbd": false, "nova::compute::image_type_exclude_list": [], "nova_permit_post_copy": true, "nova_permit_auto_converge": true, "nova_libvirt_max_queues": 0, "nova_glance_enable_rbd_download": false, "nova_glance_rbd_download_multistore_id": "", "nova::compute::force_raw_images": true, "nova::compute::use_cow_images": true, "nova::compute::libvirt::preallocate_images": "none", "nova::compute::image_cache::manager_interval": 2400, "nova::compute::image_cache::remove_unused_base_images": true, "nova::compute::image_cache::remove_unused_resized_minimum_age_seconds": 3600, "nova::compute::image_cache::precache_concurrency": 1, "nova::compute::provider::custom_inventories": {}, "nova::compute::rbd::libvirt_images_rbd_glance_copy_poll_interval": 15, "nova::compute::rbd::libvirt_images_rbd_glance_copy_timeout": 600, "ContainerNovaComputeImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "ContainerNovaLibvirtConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-libvirt:17.1"}}


created_at: 2026-06-13 07:32:38.599350
data:
  {"ContainerNovaImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "NovaServiceName": "compute"}


created_at: 2026-06-13 07:32:38.738575
data:
  {"type": "json", "value": {"ContainerNovaImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1"}}


created_at: 2026-06-13 07:32:41.591882
data:
  {"container_config_scripts": {"container_puppet_apply.sh": {"mode": "0700", "content": "#!/bin/bash\nset -eux\nSTEP=$1\nTAGS=$2\nCONFIG=$3\nEXTRA_ARGS=${4:-''}\nif [ -d /tmp/puppet-etc ]; then\n  # ignore copy failures as these may be the same file depending on docker mounts\n  cp -a /tmp/puppet-etc/* /etc/puppet || true\nfi\necho \"{\\\"step\\\": ${STEP}}\" > /etc/puppet/hieradata/docker_puppet.json\n# $::deployment_type in puppet-tripleo\nexport FACTER_deployment_type=containers\nset +e\npuppet apply $EXTRA_ARGS \\\n    --verbose \\\n    --detailed-exitcodes \\\n    --summarize \\\n    --color=false \\\n    --modulepath /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules \\\n    --tags $TAGS \\\n    -e \"noop_resource('package'); ${CONFIG}\"\nrc=$?\nset -e\nset +ux\nif [ $rc -eq 2 -o $rc -eq 0 ]; then\n    exit 0\nfi\nexit $rc\n"}, "pyshim.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n#\n# Usage: pyshim.sh <script and/or arguments>\n#\n# Unfortunately THT doesn't know which version of python might be in a\n# container so we need this script to be able to try python3 or python2\n# depending on availability.  Since this is a temporary shim until we've\n# fully cut over to python3, we check for the existence of python3 first\n# before falling back to python2. This will help in the transition from\n# python2 based containers to python3.\n\nshow_usage() {\n    echo \"Usage: pyshim.sh <script and/or arguments>\"\n}\n\nif [ $# -lt 1 ]\nthen\n    show_usage\n    exit 1\nfi\n\nset -x\nif command -v python3 >/dev/null; then\n    python3 \"$@\"\nelif command -v python2 >/dev/null; then\n    python2 \"$@\"\nelif command -v python >/dev/null; then\n    python \"$@\"\nelse\n    echo \"ERROR: python is not available!\"\n    exit 1\nfi\n"}, "pacemaker_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nset -u\n\n# ./pacemaker_restart_bundle.sh mysql galera galera-bundle Master _\n# ./pacemaker_restart_bundle.sh redis redis redis-bundle Slave Master\n# ./pacemaker_restart_bundle.sh ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\nRESTART_SCRIPTS_DIR=$(dirname $0)\nTRIPLEO_SERVICE=$1\nRESOURCE_NAME=$2\nBUNDLE_NAME=$3\nWAIT_TARGET_LOCAL=$4\nWAIT_TARGET_ANYWHERE=${5:-_}\nTRIPLEO_MINOR_UPDATE=\"${TRIPLEO_MINOR_UPDATE:-false}\"\nTRIPLEO_HA_WRAPPER_RESOURCE_EXISTS=\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS:-false}\"\n\nbundle_can_be_restarted() {\n    local bundle=$1\n    # As long as the resource bundle is managed by pacemaker and is\n    # not meant to stay stopped, no matter the state of any inner\n    # pcmk_remote or ocf resource, we should restart it to give it a\n    # chance to read the new config.\n    [ \"$(crm_resource --meta -r $1 -g is-managed 2>/dev/null)\" != \"false\" ] && \\\n    [ \"$(crm_resource --meta -r $1 -g target-role 2>/dev/null)\" != \"Stopped\" ]\n}\n\nlog() {\n    local msg=$1\n    logger -t pcmkrestart \"$1\"\n}\n\nHOSTNAME=$(/bin/hostname -s)\nif [ x\"${TRIPLEO_MINOR_UPDATE,,}\" != x\"true\" ]; then\n    if [ x\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS,,}\" = x\"false\" ]; then\n        # Do not restart during initial deployment, as the resource\n        # has just been created.\n        SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n        if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n            log \"Initial deployment, skipping the restart of ${BUNDLE_NAME}\"\n\tfi\n        exit 0\n    else\n        # During a stack update, this script is called in parallel on\n        # every node the resource runs on, after the service's configs\n        # have been updated on all nodes. So we need to run pcs only\n        # once (e.g. on the service's boostrap node).\n        if bundle_can_be_restarted ${BUNDLE_NAME}; then\n            SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n            if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n                replicas_running=$(crm_resource -Q -r $BUNDLE_NAME --locate 2>&1 | wc -l)\n                if [ \"$replicas_running\" != \"0\" ]; then\n                    log \"Restarting ${BUNDLE_NAME} globally. Stopping:\"\n                    /sbin/pcs resource disable --wait=600 $BUNDLE_NAME\n                    log \"Restarting ${BUNDLE_NAME} globally. Starting:\"\n                    /sbin/pcs resource enable --wait=600 $BUNDLE_NAME\n                else\n                    log \"${BUNDLE_NAME} is not running anywhere,\" \\\n                         \"cleaning up to restart it globally if necessary\"\n                    /sbin/pcs resource cleanup $BUNDLE_NAME\n                fi\n            else\n                log \"Skipping global restart of ${BUNDLE_NAME} on ${HOSTNAME} it will be restarted by node ${SERVICE_NODEID}\"\n            fi\n\n        else\n            log \"No global restart needed for ${BUNDLE_NAME}.\"\n        fi\n    fi\nelse\n    # During a minor update workflow however, a host gets fully\n    # updated before updating the next one. So unlike stack\n    # update, at the time this script is called, the service's\n    # configs aren't updated on all nodes yet. So only restart the\n    # resource locally, where it's guaranteed that the config is\n    # up to date.\n    HOST=$(facter hostname)\n\n    if bundle_can_be_restarted ${BUNDLE_NAME}; then\n\t# if the resource is running locally, restart it\n\tif crm_resource -r $BUNDLE_NAME --locate 2>&1 | grep -w -q \"${HOST}\"; then\n            log \"Restarting ${BUNDLE_NAME} locally on '${HOST}'\"\n            /sbin/pcs resource restart $BUNDLE_NAME \"${HOST}\"\n\n\telse\n\t    # At this point, if no resource is running locally, it's\n\t    # either because a) it has failed previously, or b) because\n\t    # it's an A/P resource running elsewhere.\n\t    # By cleaning up resource, we ensure that a) it will try to\n\t    # restart, or b) it won't do anything if the resource is\n\t    # already running elsewhere.\n            log \"${BUNDLE_NAME} is currently not running on '${HOST}',\" \\\n                 \"cleaning up its state to restart it if necessary\"\n            /sbin/pcs resource cleanup $BUNDLE_NAME node=\"${HOST}\"\n\tfi\n\n\t# Wait until the resource is in the expected target state\n\t$RESTART_SCRIPTS_DIR/pacemaker_wait_bundle.sh \\\n            $RESOURCE_NAME $BUNDLE_NAME \\\n            \"$WAIT_TARGET_LOCAL\" \"$WAIT_TARGET_ANYWHERE\" \\\n\t    \"${HOST}\" 600\n    else\n        log \"No restart needed for ${BUNDLE_NAME}.\"\n    fi\nfi\n"}, "pacemaker_wait_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# ----\n# Wait for an OCF resource or a bundle to be restarted\n# ----\n# e.g.:\n# M/S OCF:      $0 galera galera-bundle Master\n# clone OCF:    $0 rabbitmq rabbitmq-bundle Started\n# A/P M/S OCF:  $0 redis redis-bundle Slave Master\n# A/P bundle:   $0 openstack-cinder-volume openstack-cinder-volume _ Started\n# clone bundle: $0 haproxy-bundle haproxy-bundle Started\n\n# design note 1:\n#  - this script is called during a minor update; it is called\n#    once per node that hosts a service replica.\n#  - the purpose of this script is to ensure that restarting the\n#    service replica locally won't disrupt the service availability\n#    for the end user. To reach that goal, the script waits until the\n#    service is restarted locally or globallu and reaches a given\n#    target state (i.e. Started, Slave or Master).\n# design note 2:\n#   - we don't want to track restart error: our only job is to ensure\n#     service restart synchronization, not service health.\n#   - In particular, we don't want to error out in case the resource\n#     cannot be restarted locally, because that would make the minor\n#     update fail, even if potentially other replicas still provide\n#     the service.\n# design note 3:\n#   - we can bail out early if we determine that the resource can't\n#     be restarted automatically by pacemaker (e.g. its \"blocked\",\n#     unmanaged or disabled).\n\nlog() {\n    local msg=$1\n    echo \"$(date -u): $1\"\n}\n\nusage() {\n    echo 2>&1 \"Usage: $0 NAME BUNDLE_NAME ROLE_LOCAL [ROLE_ANYWHERE] [HOST] [TIMEOUT]\"\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\n#\n# Utility functions to detect stuck resources\n#\n\nbundle_failures_locally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local replica_name\n    for i in $(seq 0 $last); do\n\treplica_name=${BUNDLE_NAME}-${engine}-${i}\n\tcrm_failcount -q -G -r $replica_name -N $HOST\n    done\n}\n\nbundle_failures_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    for i in $(seq 0 $last); do\n\tcrm_failcount -q -G -r ${BUNDLE_NAME}-${engine}-${i}\n    done\n}\n\nbundle_running_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    # return the number of running bundles replica, i.e. the number of\n    # docker/podman resource replicas currently running in the cluster\n    crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:heartbeat:${engine}']/node)\" -\n}\n\nocf_failures_globally() {\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local bundle_node\n    for i in $(seq 0 $last); do\n\tbundle_node=${BUNDLE_NAME}-${i}\n\tcrm_failcount -q -G -r $NAME -N $bundle_node\n    done\n}\n\ndid_resource_failed_locally() {\n    local failures\n    local running\n    local remotehost\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# if we're dealing with an ocf resource, it is running on a\n\t# pacemaker_remote rather that on the real host, and the\n\t# failcounts are thus associated to the pcmk remote. Replace\n\t# the host's name with the pcmk remote's name.\n        remotehost=$(crm_mon --as-xml | xmllint --xpath \"string(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource/node[@name='${HOST}']/../../resource[@resource_agent='${OCF}:pacemaker:remote']/@id)\" -)\n\tif [ -n \"${remotehost}\" ]; then\n\t    crm_failcount -q -G -r $NAME -N $remotehost | grep -q -w INFINITY\n\t    return $?\n\tfi\n\t# If no pcmk remote is currently running, the failcount from\n\t# the ocf resource is useless, compute the failcount from the\n\t# bundle case instead (computed below).\n    fi\n\n    # for bundles, pacemaker can run any bundle replica locally\n    # (e.g. galera-bundle-docker-{0,1,2}), and a failure happens when\n    # there are no more replica to try.\n    # That is, when _at least_ one replica failed locally, and all the\n    # others either failed or are currently running elsewhere.\n    failures=$(bundle_failures_locally $HOST | grep -c -w INFINITY)\n    running=$(bundle_running_globally)\n    test $failures -gt 0 && \\\n    test $(( $failures + $running )) -ge $BUNDLE_REPLICAS\n}\n\ndid_resource_failed_globally() {\n    local remotecount\n    local failures\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# we check the state of an ocf resource only if the\n\t# pcmkremotes are started\n        remotecount=$(crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node)\" -)\n\tif [ \"${remotecount}\" = \"0\" ]; then\n\t    # no pcmkremote is running, so check the bundle state\n\t    # instead of checking the ocf resource\n\t    # bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\t    failures=$(bundle_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\telse\n\t    # ocf resource failed if it failed to start on\n\t    # all $BUNDLE_REPLICAS bundle nodes\n\t    failures=$(ocf_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\tfi\n    else\n\t# bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\tfailures=$(bundle_failures_globally | grep -c -w INFINITY)\n\ttest $failures -eq $BUNDLE_REPLICAS\n    fi\n}\n\n\n# Input validation\n#\n\nNAME=$1\nif [ -z \"${NAME}\" ]; then\n    echo 2>&1 \"Error: argument NAME must not be empty\"\n    exit 1\nfi\n\nBUNDLE_NAME=$2\nif [ -z \"${BUNDLE_NAME}\" ]; then\n    echo 2>&1 \"Error: argument BUNDLE_NAME must not be empty\"\n    exit 1\nfi\n\nROLE_LOCAL=$3\nif [ \"${ROLE_LOCAL}\" = \"_\" ]; then\n    ROLE_LOCAL=\"\"\nfi\n\nROLE_ANYWHERE=$4\nif [ \"${ROLE_ANYWHERE}\" = \"_\" ]; then\n    ROLE_ANYWHERE=\"\"\nfi\n\nif [ -z \"${ROLE_LOCAL}\" ]; then\n    if [ -z \"${ROLE_ANYWHERE}\" ]; then\n        echo 2>&1 \"Error: either ROLE_LOCAL or ROLE_ANYWHERE must be non empty\"\n        exit 1\n    fi\nelse\n    if !(echo \"${ROLE_LOCAL}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n        echo 2>&1 \"Error: argument ROLE_LOCAL must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n        exit 1\n    fi\nfi\n\nif [ -n \"${ROLE_ANYWHERE}\" ] && !(echo \"${ROLE_ANYWHERE}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n    echo 2>&1 \"Error: argument ROLE_ANYWHERE must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n    exit 1\nfi\n\n# Ensure compatibility with pacemaker 2.1\nif pacemaker_supports_promoted; then\n    ROLE_LOCAL=$(echo \"$ROLE_LOCAL\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    ROLE_ANYWHERE=$(echo \"$ROLE_ANYWHERE\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    OCF=\"ocf\"\nelse\n    OCF=\"ocf:\"\nfi\n\nHOST=${5:-$(facter hostname)}\nTIMEOUT=${6:-600}\n\n\n# Configure the search\n# ----\n# Note: we can't use crm_resource in all searches because we can't\n# easily extract the host the OCF resources run on (crm_resource\n# returns the pcmk-remote nodes rather than the hosts)\n# So instead, we implement various searches with XPath directly.\n\nif [ \"${BUNDLE_NAME}\" != \"${NAME}\" ]; then\n# ocf resource\nlocal_resource_xpath=\"//bundle/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node[@name='${HOST}']/../../resource[@id='${NAME}']\"\nany_resource_xpath=\"//bundle//resource[@id='${NAME}']\"\nreplicas_xpath=\"//bundle/primitive[@id='${BUNDLE_NAME}']/../*[boolean(@image) and boolean(@replicas)]\"\nelse\n# bundle resource\nlocal_resource_xpath=\"//bundle[@id='${NAME}']/replica/resource/node[@name='${HOST}']/../../resource\"\nany_resource_xpath=\"//bundle[@id='${NAME}']//resource\"\nreplicas_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nfi\n\nbundle_def_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nBUNDLE_CONTAINER_ENGINE=$(cibadmin -Q | xmllint --xpath \"name(${bundle_def_xpath})\" -)\nBUNDLE_REPLICAS=$(cibadmin -Q | xmllint --xpath \"string(${bundle_def_xpath}/@replicas)\" -)\n\n\n# The wait algorithm follows a two-stage approach\n#  1. Depending on how the script is called, we first check whether\n#     the resource is restarted locally. An A/P resource may be\n#     restarted elsewhere in the cluster.\n#  2. If needed, check whether the A/P resource has restarted\n#     elsewhere. For A/P M/S resources, in case the resource is\n#     restarted as Slave locally, ensure a Master is available.\n\nsuccess=1\nbailout=1\ntimeout=$TIMEOUT\nrole=\"\"\n\n# Stage 1: local check\nif [ -n \"$ROLE_LOCAL\" ]; then\n    log \"Waiting until ${NAME} has restarted on ${HOST} and is in state ${ROLE_LOCAL}\"\n    log \"Will probe resource state with the following XPath pattern: ${local_resource_xpath}\"\n\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resource=$(crm_mon -r --as-xml | xmllint --xpath \"${local_resource_xpath}\" - 2>/dev/null)\n        role=$(echo \"${resource}\" | sed -ne 's/.*\\Wrole=\"\\([^\"]*\\)\".*/\\1/p')\n\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resource}\" | grep -q -w \"\\Wblocked=\\\"true\\\"\"; then\n            log \"${NAME} is blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_locally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif [ \"$role\" = \"$ROLE_LOCAL\" ]; then\n            success=0\n            continue\n        elif [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" = \"$ROLE_ANYWHERE\" ]; then\n            # A/P: we are restarted in the expected state\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_LOCAL} on ${HOST}\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\n# Stage 2: global check\nif [ $timeout -gt 0 ] && [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" != \"$ROLE_ANYWHERE\" ]; then\n    log \"Wait until ${NAME} is restarted anywhere in the cluster in state ${ROLE_ANYWHERE}\"\n    log \"Will probe resource state with the following XPath pattern: ${any_resource_xpath}\"\n\n    success=1\n    bailout=1\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resources=$(crm_mon -r --as-xml | xmllint --xpath \"${any_resource_xpath}\" - 2>/dev/null)\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif ! (echo \"${resources}\" | grep -q -w \"\\Wblocked=\\\"false\\\"\"); then\n            log \"${NAME} blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_globally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resources}\" | grep -q -w \"\\Wrole=\\\"${ROLE_ANYWHERE}\\\"\"; then\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_ANYWHERE} anywhere in the cluster\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\nif [ $timeout -le 0 ]; then\n    log \"Timeout reached after ${TIMEOUT}s while waiting for ${NAME} to be restarted\"\nelif [ $bailout -le 0 ]; then\n    log \"Restart monitoring for ${NAME} cancelled\"\nfi\n\nif [ $success -eq 0 ]; then\n    log \"${NAME} successfully restarted\"\nelse\n    log \"${NAME} was not restarted properly\"\nfi\n\n# Don't block minor update or stack update if the wait was unsuccessful\nexit 0\n"}, "wait-port-and-run.sh": {"mode": "0755", "content": "#!/usr/bin/bash\nset -eu\n\nHOST=$1\nPORT=$2\n\necho \"$(date -u): Checking whether we can bind to ${HOST}:${PORT}\"\nwhile (ss -Htnl src \"${HOST}\" \"sport = :${PORT}\" | grep -wq \"${PORT}\"); do\n    echo \"$(date -u): ${HOST}:${PORT} still in use, waiting...\";\n    sleep 10;\ndone\n\nshift 2\nCOMMAND=\"$*\"\nif [ -z \"${COMMAND}\" ]; then\n    COMMAND=\"true\"\nfi\nexec $COMMAND\n"}, "pacemaker_resource_lock.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nMAX_RETRIES=10\nCIB_ENOTFOUND=105\n\nusage() {\n   echo \"Set a global property in the cluster with a validity timestamp.\"\n   echo \"Usage:\"\n   echo \"   $0 --acquire <lock_name> <lock_owner> <lock_ttl_in_seconds>\"\n   echo \"   $0 --release <lock_name> <lock_owner>\"\n   echo\n}\n\nlog() {\n    echo \"$(date -u): $1\" 1>&2\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\nlock_create() {\n    local name=$1\n    local data=$2\n    # cibadmin won't overwrite a key if someone else succeeded to create it concurrently\n    cibadmin --sync-call --scope crm_config --create --xml-text \"<cluster_property_set id='${name}'><nvpair id='${name}-pair' name='${name}' value='${data}'/></cluster_property_set>\" &>/dev/null\n    return $?\n}\n\nlock_update() {\n    local name=$1\n    local expected_data=$2\n    local new_data=$3\n    # we only update the lock we expect to see, so we can't update someone else's lock\n    cibadmin --sync-call --scope crm_config --modify --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" --xml-text \"<nvpair id='${name}-pair' name='${name}' value='${new_data}'/>\" &>/dev/null\n    return $?\n}\n\nlock_delete() {\n    local name=$1\n    local expected_data=$2\n    # we only delete the lock we expect to see, so we can't delete someone else's lock\n    cibadmin --sync-call --scope crm_config --delete --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" &>/dev/null\n    return $?\n}\n\nlock_get() {\n    local lockname=$1\n    local res\n    local rc\n    res=$(cibadmin --query --scope crm_config --xpath \"//cluster_property_set/nvpair[@name='$lockname']\" 2>/dev/null)\n    rc=$?\n    if [ $rc -eq 0 ]; then\n        echo \"$res\" | sed -n 's/.*value=\"\\([^\"]*\\)\".*/\\1/p'\n    fi\n    return $rc\n}\n\nlock_owner() {\n    local lock=$1\n    echo \"$lock\" | cut -d':' -f1\n}\n\nlock_has_expired() {\n    local lock=$1\n    local expiry=$(echo \"$lock\" | cut -d':' -f2)\n    local now=$(date +%s)\n    test $now -ge $expiry\n}\n\n\n# Perform a lock action and restart if the CIB has been modified before\n# committing the lock action\ntry_action() {\n    local fun=$1\n    local lock=$2\n    local requester=$3\n    local args=${4:-}\n    local tries=$MAX_RETRIES\n    local rc=1\n    if [ \"$fun\" = \"lock_acquire\" ] || [ \"$fun\" = \"lock_release\" ]; then\n        log \"Try running $fun\"\n    else\n        return 2\n    fi\n    while [ $rc -ne 0 ]; do\n        $fun $lock $requester $args\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Operation $1 succeeded\"\n            return 0\n        elif [ $rc -eq 3 ]; then\n            # rc == 3 -> CIB changed before push\n            if [ $tries -eq 0 ]; then\n                log \"Failed to commit after $MAX_RETRIES retries. Bailing out.\"\n                return 2\n            else\n                log \"Failed to commit. Retrying operation.\"\n                tries=$(($tries - 1))\n            fi\n        elif [ $rc -eq 2 ]; then\n            # rc == 2 -> unrecoverable cib error (e.g. pacemaker down)\n            log \"Unexpected failure. Bailing out\"\n            return $rc\n        else\n            # rc == 1 -> lock error (not owner, lock doesn't exists)\n            return $rc\n        fi\n    done\n}\n\n# The lock mechanism uses cibadmin's atomic creation so cluster-wide\n# state coherency is guaranteed by pacemaker\nlock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc\n    local lock\n    local expiry\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -n \"$lock\" ]; then\n        lock_has_expired $lock\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Lock has expired, now available for being held\"\n        else\n            # lock is still held. check whether we're the owner\n            owner=$(lock_owner $lock)\n            if [ \"$owner\" = \"$requester\" ];then\n                log \"Requester already owns the lock, acquiring attempt will just reconfigure the TTL\"\n            else\n                log \"Lock is held by someone else ($owner)\"\n                return 1\n            fi\n        fi\n    else\n        log \"Lock is not held yet\"\n    fi\n\n    # prepare the lock info\n    expiry=$(($(date +%s) + $ttl))\n\n    if [ -n \"$lock\" ]; then\n        log \"Attempting to update the lock\"\n        lock_update $lockname \"$lock\" \"$requester:$expiry\"\n        rc=$?\n    else\n        log \"Attempting to acquire the lock\"\n        lock_create $lockname \"$requester:$expiry\"\n        rc=$?\n    fi\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' acquired by '$requester', valid until $(date -d @$expiry)\"\n        return 0\n    else\n        log \"CIB changed, lock cannot be acquired\"\n        return 3\n    fi\n}\n\n\n# The lock mechanism uses the CIB's num_updates tag to implement\n# a conditional store. Cluster-wide locking is guaranteed by pacemaker\nlock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n    local lock\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        log \"Lock doesn't exist. Nothing to release\"\n        return 0\n    else\n        log \"Lock exists, check whether we're the owner\"\n        owner=$(lock_owner $lock)\n        if [ \"$owner\" != \"$requester\" ];then\n            log \"Lock is held by someone else ($owner), will not unlock\"\n            return 1\n        fi\n    fi\n\n    lock_delete $lockname \"$lock\"\n    rc=$?\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' released by '$requester'\"\n        return 0\n    else\n        log \"CIB deletion error, lock cannot be released\"\n        return 3\n    fi\n}\n\n\n# Retrieve the owner of a lock from the CIB\n# this is a read-only operation, so no need to log debug info\nlock_get_owner() {\n    local lockname=$1\n    local rc\n    local lock\n    local owner\n\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        return 1\n    else\n        lock_owner $lock\n        return 0\n    fi\n}\n\n\nACTION=$1\nLOCKNAME=$2\nREQUESTER=$3\nTTL=${4:-60}\n\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nif [ $ACTION != \"--help\" ]; then\n    if [ -z \"$LOCKNAME\" ]; then\n        error \"You must specific a lock name\"\n    fi\n    if [ $ACTION != \"--owner\" ] && [ $ACTION != \"-o\" ]; then\n        if [ -z \"$REQUESTER\" ]; then\n            error \"You must specific a lock requester\"\n        fi\n    fi\nfi\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) try_action lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --release|-r) try_action lock_release $LOCKNAME $REQUESTER;;\n    --acquire-once|-A) lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --owner|-o) lock_get_owner $LOCKNAME;;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "pacemaker_mutex_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_restart_bundle.sh --lock mysql galera galera-bundle Master _\n# pacemaker_mutex_restart_bundle.sh --lock ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\n\nset -u\n\nusage() {\n    echo \"Restart a clustered resource in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --lock <tripleo-service> <pcmk-resource> <pcmk-bundle> <target-state-local> <target-state-cluster>\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\nACTION=$1\ncase $ACTION in\n    --help) usage; exit 0;;\n    --lock) ;;\n    *) error \"Unknown action '$ACTION'\";;\nesac\n\nTRIPLEO_SERVICE=$2\nLOCK_NAME=${TRIPLEO_SERVICE}-restart-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to restart resource $TRIPLEO_SERVICE\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\nRESOURCE_NAME=$3\nBUNDLE_NAME=$4\nWAIT_TARGET_LOCAL=$5\nWAIT_TARGET_ANYWHERE=${6:-_}\n\nif pacemaker_supports_promoted; then\n    WAIT_TARGET_LOCAL=$(echo \"$5\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    WAIT_TARGET_ANYWHERE=$(echo \"${6:-_}\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    promoted_role=\"Promoted\"\nelse\n    promoted_role=\"Master\"\nfi\n\n# The lock TTL should accomodate for the resource start/promote timeout\nif [ \"$RESOURCE_NAME\" != \"$BUNDLE_NAME\" ]; then\n    if [ \"$WAIT_TARGET_LOCAL\" = \"$promoted_role\" ] || [ \"$WAIT_TARGET_ANYWHERE\" = \"$promoted_role\" ]; then\n        rsc_op=\"promote\"\n    else\n        rsc_op=\"start\"\n    fi\n    # <op id=\"galera-promote-interval-0s\" interval=\"0s\" name=\"promote\" on-fail=\"block\" timeout=\"300s\"/>\n    PCMK_TTL=$(cibadmin -Q | xmllint -xpath \"string(//primitive[@id='${RESOURCE_NAME}']/operations/op[@name='${rsc_op}']/@timeout)\" - | sed 's/s$//')\n    LOCK_TTL=$((PCMK_TTL + 30))\nelse\n    # The podman RA's default start timeout\n    LOCK_TTL=90\nfi\n\nlog \"Acquire a ${LOCK_TTL}s restart lock for service $TRIPLEO_SERVICE before restarting it\"\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nrc=1\nwhile [ $rc -ne 0 ]; do\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire $LOCK_NAME $LOCK_OWNER $LOCK_TTL\n    rc=$?\n    if [ $rc != 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not acquire lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Could not acquire lock, retrying\"\n            sleep 10\n        fi\n    fi\ndone\n\nlog \"Restart the service $TRIPLEO_SERVICE locally\"\n# Reuse the local restart script in t-h-t (driven by env var TRIPLEO_MINOR_UPDATE)\nTRIPLEO_MINOR_UPDATE=true /var/lib/container-config-scripts/pacemaker_restart_bundle.sh $TRIPLEO_SERVICE $RESOURCE_NAME $BUNDLE_NAME $WAIT_TARGET_LOCAL $WAIT_TARGET_ANYWHERE\n\n# If we reached this point, always try to release the lock\nlog \"Release the restart lock for service $TRIPLEO_SERVICE\"\n/var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $LOCK_NAME $LOCK_OWNER\nrc=$?\nif [ $rc -ne 0 ] && [ $rc -ne 1 ]; then\n    error \"Could not release held lock (rc: $rc)\"\nfi\n"}, "pacemaker_mutex_shutdown.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_shutdown.sh --acquire\n# pacemaker_mutex_shutdown.sh --release\n\nset -u\n\nusage() {\n    echo \"Shutdown a cluster node in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --acquire # prevent other node from shutting down until we hold the lock\"\n    echo \"   $0 --release # release the lock, other node can compete for the shutdown lock\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nshutdown_lock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc=1\n    local current_owner\n    local owner_stopped\n    local owner_rc\n\n    log \"Acquiring the shutdown lock\"\n    while [ $rc -ne 0 ]; do\n        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire-once $lockname $requester $ttl\n        rc=$?\n        if [ $rc -ne 0 ]; then\n            if [ $rc -eq 2 ]; then\n                error \"Could not acquire the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n            else\n                # The lock is held by another node.\n                current_owner=$(/var/lib/container-config-scripts/pacemaker_resource_lock.sh --owner $lockname)\n                owner_rc=$?\n                if [ $owner_rc -eq 2 ]; then\n                    error \"Could not get the shutdown lock owner due to unrecoverable error (rc: $owner_rc), bailing out\"\n                fi\n                if [ $owner_rc -eq 0 ]; then\n                    # If the owner is marked as offline, that means it has shutdown and\n                    # we can clean the lock preemptively and try to acquire it.\n                    owner_stopped=$(crm_mon -1X | xmllint --xpath 'count(//nodes/node[@name=\"'${current_owner}'\" and @online=\"false\" and @unclean=\"false\"])' -)\n                    if [ \"${owner_stopped}\" = \"1\" ]; then\n                        log \"Shutdown lock held by stopped node '${current_owner}', lock can be released\"\n                        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $current_owner\n                        continue\n                    fi\n                fi\n                log \"Shutdown lock held by another node (rc: $rc), retrying\"\n                sleep 10\n            fi\n        fi\n    done\n    log \"Shutdown lock acquired\"\n    return 0\n}\n\n\n# Release the lock if we still own it. Not owning it anymore is not fatal\nshutdown_lock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n\n    log \"Releasing the shutdown lock\"\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $requester\n    rc=$?\n    if [ $rc -ne 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not release the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Shutdown lock no longer held, nothing to do\"\n        fi\n    else\n        log \"Shutdown lock released\"\n    fi\n    return 0\n}\n\n\nACTION=$1\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nLOCK_NAME=tripleo-shutdown-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to aquire the shutdown lock\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\n# We start with a very high TTL, that long enough to accomodate a cluster stop.\n# As soon as the node will get offline, the other competing node will be entitled\n# to steal the lock, so they should never wait that long in practice.\nLOCK_TTL=600\n\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) shutdown_lock_acquire ${LOCK_NAME} ${LOCK_OWNER} ${LOCK_TTL};;\n    --release|-r) shutdown_lock_release ${LOCK_NAME} ${LOCK_OWNER};;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}}, "healthcheck_rpc_port": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro"], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-38-qqzikclnisao-ContainersCommon-gmsskdnesw5s/a0fbd289-f8be-410c-a380-139c9534ac0f"}


created_at: 2026-06-13 07:32:41.657233
data:
  {"tuple:('role_data', 'config_settings', 'nova::logging::debug')": true, "tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": ""}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-38-qqzikclnisao-NovaBase-nyk6emnwuffp/6782c6f2-51c8-4318-af24-2753f55dbe2c"}


created_at: 2026-06-13 07:32:41.749615
data:
  {"cinder_nvmeof_volumes": null, "cinder_nvmeof_host_prep_tasks": null, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-38-qqzikclnisao-CinderNVMeOF-otjc654q3igi/804fa6b2-893b-43a8-a3da-4194fcb9512d"}


created_at: 2026-06-13 07:32:41.851262
data:
  {"tuple:('docker_config', 'step_2')": {"nova_compute_init_log": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "cmd_extra_args": "", "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "setype": "{{ item.setype }}", "state": "directory", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}]}], "config_settings": null, "volumes": ["/var/log/containers/nova:/var/log/nova"], "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-38-qqzikclnisao-NovaLogging-vrwi5u5td5iv/13dcf063-71dc-4363-945e-7a6a6a498bbb"}


created_at: 2026-06-13 07:32:41.895226
data:
  {"nova_compute_common_deploy_steps_tasks": [{"block": [{"name": "is additonal Cell?", "set_fact": {"nova_additional_cell": false}}, {"name": "check if discover hosts is required", "when": ["not nova_additional_cell|bool", "nova_cellv2_discovery_done is not defined"], "block": [{"name": "discover via nova_manager?", "set_fact": {"nova_cellv2_discovery_delegate_host": "{{ groups['nova_manager'][0] }}", "nova_cellv2_discovery_container": "nova_manager"}, "when": ["groups['nova_manager'] is defined and (groups['nova_manager']|length>0)"]}, {"name": "discover via nova_api?", "set_fact": {"nova_cellv2_discovery_delegate_host": "{{ groups['nova_api'][0] }}", "nova_cellv2_discovery_container": "nova_api"}, "when": ["nova_cellv2_discovery_delegate_host is not defined", "groups['nova_api'] is defined and (groups['nova_api']|length>0)"]}, {"name": "Warn if no discovery host available", "fail": {"msg": "No hosts available to run nova cell_v2 host discovery."}, "ignore_errors": true, "when": ["nova_cellv2_discovery_delegate_host is not defined"]}, {"name": "Discovering nova hosts", "command": "{{ container_cli }} exec {{ nova_cellv2_discovery_container }} nova-manage cell_v2 discover_hosts --by-service", "become": true, "changed_when": false, "delegate_to": "{{ nova_cellv2_discovery_delegate_host }}", "when": ["nova_cellv2_discovery_delegate_host is defined"]}, {"set_fact": {"nova_cellv2_discovery_done": true}}]}]}], "container_config_scripts": {"nova_statedir_ownership.py": {"mode": "0700", "content": "#!/usr/bin/python3\n#\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\nimport logging\nimport os\nimport pwd\nimport selinux\nimport stat\nimport sys\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_statedir')\n\n\nclass PathManager(object):\n    \"\"\"Helper class to manipulate ownership of a given path\"\"\"\n    def __init__(self, path):\n        self.path = path\n        self.uid = None\n        self.gid = None\n        self.is_dir = None\n        self.secontext = None\n        self._update()\n\n    def _update(self):\n        try:\n            statinfo = os.stat(self.path)\n            self.is_dir = stat.S_ISDIR(statinfo.st_mode)\n            self.uid = statinfo.st_uid\n            self.gid = statinfo.st_gid\n            self.secontext = selinux.lgetfilecon(self.path)[1]\n        except Exception:\n            LOG.exception('Could not update metadata for %s', self.path)\n            raise\n\n    def __str__(self):\n        return \"uid: {} gid: {} path: {}{}\".format(\n            self.uid,\n            self.gid,\n            self.path,\n            '/' if self.is_dir else ''\n        )\n\n    def has_owner(self, uid, gid):\n        return self.uid == uid and self.gid == gid\n\n    def has_either(self, uid, gid):\n        return self.uid == uid or self.gid == gid\n\n    def chown(self, uid, gid):\n        target_uid = -1\n        target_gid = -1\n        if self.uid != uid:\n            target_uid = uid\n        if self.gid != gid:\n            target_gid = gid\n        if (target_uid, target_gid) != (-1, -1):\n            LOG.info('Changing ownership of %s from %d:%d to %d:%d',\n                     self.path,\n                     self.uid,\n                     self.gid,\n                     self.uid if target_uid == -1 else target_uid,\n                     self.gid if target_gid == -1 else target_gid)\n            try:\n                os.chown(self.path, target_uid, target_gid)\n                self._update()\n            except Exception:\n                LOG.exception('Could not change ownership of %s: ',\n                              self.path)\n                raise\n        else:\n            LOG.info('Ownership of %s already %d:%d',\n                     self.path,\n                     uid,\n                     gid)\n\n    def chcon(self, context):\n        # If dir returns whether to recusively set context\n        try:\n            try:\n                selinux.lsetfilecon(self.path, context)\n                LOG.info('Setting selinux context of %s to %s',\n                     self.path, context)\n                return True\n            except OSError as e:\n                if self.is_dir and e.errno == 95:\n                    # Operation not supported, assume NFS mount and skip\n                    LOG.info('Setting selinux context not supported for %s',\n                             self.path)\n                    return False\n                else:\n                    raise\n        except Exception:\n            LOG.exception('Could not set selinux context of %s to %s:',\n                          self.path, context)\n            raise\n\n\nclass NovaStatedirOwnershipManager(object):\n    \"\"\"Class to manipulate the ownership of the nova statedir (/var/lib/nova).\n\n       The nova uid/gid differ on the host and container images. An upgrade\n       that switches from host systemd services to docker requires a change in\n       ownership. Previously this was a naive recursive chown, however this\n       causes issues if nova instance are shared via an NFS mount: any open\n       filehandles in qemu/libvirt fail with an I/O error (LP1778465).\n\n       Instead the upgrade/FFU ansible tasks now lay down a marker file when\n       stopping and disabling the host systemd services. We use this file to\n       determine the host nova uid/gid. We then walk the tree and update any\n       files that have the host uid/gid to the docker nova uid/gid. As files\n       owned by root/qemu etc... are ignored this avoids the issues with open\n       filehandles. The marker is removed once the tree has been walked.\n\n       For subsequent runs, or for a new deployment, we simply ensure that the\n       docker nova user/group owns all directories. This is required as the\n       directories are created with root ownership in host_prep_tasks (the\n       docker nova uid/gid is not known in this context).\n    \"\"\"\n    def __init__(self, statedir, upgrade_marker='upgrade_marker',\n                 nova_user='nova', secontext_marker='../_nova_secontext',\n                 exclude_paths=None):\n        self.statedir = statedir\n        self.nova_user = nova_user\n\n        self.upgrade_marker_path = os.path.join(statedir, upgrade_marker)\n        self.secontext_marker_path = os.path.normpath(os.path.join(statedir, secontext_marker))\n        self.upgrade = os.path.exists(self.upgrade_marker_path)\n\n        self.exclude_paths = [self.upgrade_marker_path]\n        if exclude_paths is not None:\n            for p in exclude_paths:\n                if not p.startswith(os.path.sep):\n                    p = os.path.join(self.statedir, p)\n                self.exclude_paths.append(p)\n\n        self.target_uid, self.target_gid = self._get_nova_ids()\n        self.previous_uid, self.previous_gid = self._get_previous_nova_ids()\n        self.id_change = (self.target_uid, self.target_gid) != \\\n            (self.previous_uid, self.previous_gid)\n        self.target_secontext = self._get_secontext()\n\n    def _get_nova_ids(self):\n        nova_uid, nova_gid = pwd.getpwnam(self.nova_user)[2:4]\n        return nova_uid, nova_gid\n\n    def _get_previous_nova_ids(self):\n        if self.upgrade:\n            statinfo = os.stat(self.upgrade_marker_path)\n            return statinfo.st_uid, statinfo.st_gid\n        else:\n            return self._get_nova_ids()\n\n    def _get_secontext(self):\n        if os.path.exists(self.secontext_marker_path):\n            return selinux.lgetfilecon(self.secontext_marker_path)[1]\n        else:\n            return None\n\n    def _walk(self, top, chcon=True):\n        for f in os.listdir(top):\n            pathname = os.path.join(top, f)\n\n            if pathname in self.exclude_paths:\n                continue\n\n            try:\n                pathinfo = PathManager(pathname)\n                LOG.info(\"Checking %s\", pathinfo)\n                if pathinfo.is_dir:\n                    # Always chown the directories\n                    pathinfo.chown(self.target_uid, self.target_gid)\n                    chcon_r = chcon\n                    if chcon:\n                        chcon_r = pathinfo.chcon(self.target_secontext)\n                    self._walk(pathname, chcon_r)\n                elif self.id_change:\n                    # Only chown files if it's an upgrade and the file is owned by\n                    # the host nova uid/gid\n                    pathinfo.chown(\n                        self.target_uid if pathinfo.uid == self.previous_uid\n                        else pathinfo.uid,\n                        self.target_gid if pathinfo.gid == self.previous_gid\n                        else pathinfo.gid\n                    )\n                    if chcon:\n                        pathinfo.chcon(self.target_secontext)\n            except Exception:\n                # Likely to have been caused by external systems\n                # interacting with this directory tree,\n                # especially on NFS e.g snapshot dirs.\n                # Just ignore it and continue on to the next entry\n                continue\n\n    def run(self):\n        LOG.info('Applying nova statedir ownership')\n        LOG.info('Target ownership for %s: %d:%d',\n                 self.statedir,\n                 self.target_uid,\n                 self.target_gid)\n\n        pathinfo = PathManager(self.statedir)\n        LOG.info(\"Checking %s\", pathinfo)\n        pathinfo.chown(self.target_uid, self.target_gid)\n        chcon = self.target_secontext is not None\n\n        if chcon:\n            pathinfo.chcon(self.target_secontext)\n\n        self._walk(self.statedir, chcon)\n\n        if self.upgrade:\n            LOG.info('Removing upgrade_marker %s',\n                     self.upgrade_marker_path)\n            os.unlink(self.upgrade_marker_path)\n\n        LOG.info('Nova statedir ownership complete')\n\n\ndef get_exclude_paths():\n    exclude_paths = os.environ.get('NOVA_STATEDIR_OWNERSHIP_SKIP')\n    if exclude_paths is not None:\n        exclude_paths = exclude_paths.split(os.pathsep)\n    return exclude_paths\n\n\nif __name__ == '__main__':\n    NovaStatedirOwnershipManager('/var/lib/nova', exclude_paths=get_exclude_paths()).run()\n"}, "nova_wait_for_compute_service.py": {"mode": "0755", "content": "#!/usr/bin/python3\n#\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\nimport argparse\nimport logging\nimport os\nimport six\nimport socket\nimport sys\nimport time\n\n\nfrom keystoneauth1 import loading\nfrom keystoneauth1 import session\n\nfrom novaclient import client\n\n# In python3 SafeConfigParser was renamed to ConfigParser and the default\n# for duplicate options default to true. In case of nova it is valid to\n# have duplicate option lines, e.g. passthrough_whitelist which leads to\n# issues reading the nova.conf\n# https://bugs.launchpad.net/tripleo/+bug/1827775\nif six.PY3:\n    from six.moves.configparser import ConfigParser\n    config = ConfigParser(strict=False)\nelse:\n    from six.moves.configparser import SafeConfigParser\n    config = SafeConfigParser()\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_wait_for_compute_service')\n\niterations = 60\ntimeout = 10\nnova_cfg = '/etc/nova/nova.conf'\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(usage='%(prog)s [options]')\n    parser.add_argument('-k', '--insecure',\n                        action=\"store_false\",\n                        dest='insecure',\n                        default=True,\n                        help='Allow insecure connection when using SSL')\n\n    args = parser.parse_args()\n    LOG.debug('Running with parameter insecure = %s',\n              args.insecure)\n\n    if os.path.isfile(nova_cfg):\n        try:\n            config.read(nova_cfg)\n        except Exception:\n            LOG.exception('Error while reading nova.conf:')\n    else:\n        LOG.error('Nova configuration file %s does not exist', nova_cfg)\n        sys.exit(1)\n\n    my_host = config.get('DEFAULT', 'host')\n    if not my_host:\n        # If host isn't set nova defaults to this\n        my_host = socket.gethostname()\n\n    loader = loading.get_plugin_loader('password')\n    auth = loader.load_from_options(\n        auth_url=config.get('neutron',\n                            'auth_url'),\n        username=config.get('neutron',\n                            'username'),\n        password=config.get('neutron',\n                            'password'),\n        project_name=config.get('neutron',\n                                'project_name'),\n        project_domain_name=config.get('neutron',\n                                       'project_domain_name'),\n        user_domain_name=config.get('neutron',\n                                    'user_domain_name'))\n    sess = session.Session(auth=auth, verify=args.insecure)\n    nova = client.Client('2.11', session=sess, endpoint_type='internal',\n                         region_name=config.get('neutron', 'region_name'))\n\n    # Wait until this host is listed in the service list\n    for i in range(iterations):\n        try:\n            service_list = nova.services.list(binary='nova-compute')\n            for entry in service_list:\n                host = getattr(entry, 'host', '')\n                zone = getattr(entry, 'zone', '')\n                if host == my_host and zone != 'internal':\n                    LOG.info('Nova-compute service registered')\n                    sys.exit(0)\n            LOG.info('Waiting for nova-compute service to register')\n        except Exception:\n            LOG.exception(\n                'Error while waiting for nova-compute service to register')\n        time.sleep(timeout)\nsys.exit(1)\n\n# vim: set et ts=4 sw=4 :\n"}}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-38-qqzikclnisao-NovaComputeCommon-47yqn2ww73a2/cc8bf441-f72b-48d5-8c74-33190d8c3400"}


created_at: 2026-06-13 07:32:41.922398
data:
  {"tuple:('role_data', 'step_config')": null, "tuple:('role_data', 'container_config_scripts')": {"container_puppet_apply.sh": {"mode": "0700", "content": "#!/bin/bash\nset -eux\nSTEP=$1\nTAGS=$2\nCONFIG=$3\nEXTRA_ARGS=${4:-''}\nif [ -d /tmp/puppet-etc ]; then\n  # ignore copy failures as these may be the same file depending on docker mounts\n  cp -a /tmp/puppet-etc/* /etc/puppet || true\nfi\necho \"{\\\"step\\\": ${STEP}}\" > /etc/puppet/hieradata/docker_puppet.json\n# $::deployment_type in puppet-tripleo\nexport FACTER_deployment_type=containers\nset +e\npuppet apply $EXTRA_ARGS \\\n    --verbose \\\n    --detailed-exitcodes \\\n    --summarize \\\n    --color=false \\\n    --modulepath /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules \\\n    --tags $TAGS \\\n    -e \"noop_resource('package'); ${CONFIG}\"\nrc=$?\nset -e\nset +ux\nif [ $rc -eq 2 -o $rc -eq 0 ]; then\n    exit 0\nfi\nexit $rc\n"}, "pyshim.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n#\n# Usage: pyshim.sh <script and/or arguments>\n#\n# Unfortunately THT doesn't know which version of python might be in a\n# container so we need this script to be able to try python3 or python2\n# depending on availability.  Since this is a temporary shim until we've\n# fully cut over to python3, we check for the existence of python3 first\n# before falling back to python2. This will help in the transition from\n# python2 based containers to python3.\n\nshow_usage() {\n    echo \"Usage: pyshim.sh <script and/or arguments>\"\n}\n\nif [ $# -lt 1 ]\nthen\n    show_usage\n    exit 1\nfi\n\nset -x\nif command -v python3 >/dev/null; then\n    python3 \"$@\"\nelif command -v python2 >/dev/null; then\n    python2 \"$@\"\nelif command -v python >/dev/null; then\n    python \"$@\"\nelse\n    echo \"ERROR: python is not available!\"\n    exit 1\nfi\n"}, "pacemaker_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nset -u\n\n# ./pacemaker_restart_bundle.sh mysql galera galera-bundle Master _\n# ./pacemaker_restart_bundle.sh redis redis redis-bundle Slave Master\n# ./pacemaker_restart_bundle.sh ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\nRESTART_SCRIPTS_DIR=$(dirname $0)\nTRIPLEO_SERVICE=$1\nRESOURCE_NAME=$2\nBUNDLE_NAME=$3\nWAIT_TARGET_LOCAL=$4\nWAIT_TARGET_ANYWHERE=${5:-_}\nTRIPLEO_MINOR_UPDATE=\"${TRIPLEO_MINOR_UPDATE:-false}\"\nTRIPLEO_HA_WRAPPER_RESOURCE_EXISTS=\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS:-false}\"\n\nbundle_can_be_restarted() {\n    local bundle=$1\n    # As long as the resource bundle is managed by pacemaker and is\n    # not meant to stay stopped, no matter the state of any inner\n    # pcmk_remote or ocf resource, we should restart it to give it a\n    # chance to read the new config.\n    [ \"$(crm_resource --meta -r $1 -g is-managed 2>/dev/null)\" != \"false\" ] && \\\n    [ \"$(crm_resource --meta -r $1 -g target-role 2>/dev/null)\" != \"Stopped\" ]\n}\n\nlog() {\n    local msg=$1\n    logger -t pcmkrestart \"$1\"\n}\n\nHOSTNAME=$(/bin/hostname -s)\nif [ x\"${TRIPLEO_MINOR_UPDATE,,}\" != x\"true\" ]; then\n    if [ x\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS,,}\" = x\"false\" ]; then\n        # Do not restart during initial deployment, as the resource\n        # has just been created.\n        SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n        if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n            log \"Initial deployment, skipping the restart of ${BUNDLE_NAME}\"\n\tfi\n        exit 0\n    else\n        # During a stack update, this script is called in parallel on\n        # every node the resource runs on, after the service's configs\n        # have been updated on all nodes. So we need to run pcs only\n        # once (e.g. on the service's boostrap node).\n        if bundle_can_be_restarted ${BUNDLE_NAME}; then\n            SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n            if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n                replicas_running=$(crm_resource -Q -r $BUNDLE_NAME --locate 2>&1 | wc -l)\n                if [ \"$replicas_running\" != \"0\" ]; then\n                    log \"Restarting ${BUNDLE_NAME} globally. Stopping:\"\n                    /sbin/pcs resource disable --wait=600 $BUNDLE_NAME\n                    log \"Restarting ${BUNDLE_NAME} globally. Starting:\"\n                    /sbin/pcs resource enable --wait=600 $BUNDLE_NAME\n                else\n                    log \"${BUNDLE_NAME} is not running anywhere,\" \\\n                         \"cleaning up to restart it globally if necessary\"\n                    /sbin/pcs resource cleanup $BUNDLE_NAME\n                fi\n            else\n                log \"Skipping global restart of ${BUNDLE_NAME} on ${HOSTNAME} it will be restarted by node ${SERVICE_NODEID}\"\n            fi\n\n        else\n            log \"No global restart needed for ${BUNDLE_NAME}.\"\n        fi\n    fi\nelse\n    # During a minor update workflow however, a host gets fully\n    # updated before updating the next one. So unlike stack\n    # update, at the time this script is called, the service's\n    # configs aren't updated on all nodes yet. So only restart the\n    # resource locally, where it's guaranteed that the config is\n    # up to date.\n    HOST=$(facter hostname)\n\n    if bundle_can_be_restarted ${BUNDLE_NAME}; then\n\t# if the resource is running locally, restart it\n\tif crm_resource -r $BUNDLE_NAME --locate 2>&1 | grep -w -q \"${HOST}\"; then\n            log \"Restarting ${BUNDLE_NAME} locally on '${HOST}'\"\n            /sbin/pcs resource restart $BUNDLE_NAME \"${HOST}\"\n\n\telse\n\t    # At this point, if no resource is running locally, it's\n\t    # either because a) it has failed previously, or b) because\n\t    # it's an A/P resource running elsewhere.\n\t    # By cleaning up resource, we ensure that a) it will try to\n\t    # restart, or b) it won't do anything if the resource is\n\t    # already running elsewhere.\n            log \"${BUNDLE_NAME} is currently not running on '${HOST}',\" \\\n                 \"cleaning up its state to restart it if necessary\"\n            /sbin/pcs resource cleanup $BUNDLE_NAME node=\"${HOST}\"\n\tfi\n\n\t# Wait until the resource is in the expected target state\n\t$RESTART_SCRIPTS_DIR/pacemaker_wait_bundle.sh \\\n            $RESOURCE_NAME $BUNDLE_NAME \\\n            \"$WAIT_TARGET_LOCAL\" \"$WAIT_TARGET_ANYWHERE\" \\\n\t    \"${HOST}\" 600\n    else\n        log \"No restart needed for ${BUNDLE_NAME}.\"\n    fi\nfi\n"}, "pacemaker_wait_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# ----\n# Wait for an OCF resource or a bundle to be restarted\n# ----\n# e.g.:\n# M/S OCF:      $0 galera galera-bundle Master\n# clone OCF:    $0 rabbitmq rabbitmq-bundle Started\n# A/P M/S OCF:  $0 redis redis-bundle Slave Master\n# A/P bundle:   $0 openstack-cinder-volume openstack-cinder-volume _ Started\n# clone bundle: $0 haproxy-bundle haproxy-bundle Started\n\n# design note 1:\n#  - this script is called during a minor update; it is called\n#    once per node that hosts a service replica.\n#  - the purpose of this script is to ensure that restarting the\n#    service replica locally won't disrupt the service availability\n#    for the end user. To reach that goal, the script waits until the\n#    service is restarted locally or globallu and reaches a given\n#    target state (i.e. Started, Slave or Master).\n# design note 2:\n#   - we don't want to track restart error: our only job is to ensure\n#     service restart synchronization, not service health.\n#   - In particular, we don't want to error out in case the resource\n#     cannot be restarted locally, because that would make the minor\n#     update fail, even if potentially other replicas still provide\n#     the service.\n# design note 3:\n#   - we can bail out early if we determine that the resource can't\n#     be restarted automatically by pacemaker (e.g. its \"blocked\",\n#     unmanaged or disabled).\n\nlog() {\n    local msg=$1\n    echo \"$(date -u): $1\"\n}\n\nusage() {\n    echo 2>&1 \"Usage: $0 NAME BUNDLE_NAME ROLE_LOCAL [ROLE_ANYWHERE] [HOST] [TIMEOUT]\"\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\n#\n# Utility functions to detect stuck resources\n#\n\nbundle_failures_locally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local replica_name\n    for i in $(seq 0 $last); do\n\treplica_name=${BUNDLE_NAME}-${engine}-${i}\n\tcrm_failcount -q -G -r $replica_name -N $HOST\n    done\n}\n\nbundle_failures_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    for i in $(seq 0 $last); do\n\tcrm_failcount -q -G -r ${BUNDLE_NAME}-${engine}-${i}\n    done\n}\n\nbundle_running_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    # return the number of running bundles replica, i.e. the number of\n    # docker/podman resource replicas currently running in the cluster\n    crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:heartbeat:${engine}']/node)\" -\n}\n\nocf_failures_globally() {\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local bundle_node\n    for i in $(seq 0 $last); do\n\tbundle_node=${BUNDLE_NAME}-${i}\n\tcrm_failcount -q -G -r $NAME -N $bundle_node\n    done\n}\n\ndid_resource_failed_locally() {\n    local failures\n    local running\n    local remotehost\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# if we're dealing with an ocf resource, it is running on a\n\t# pacemaker_remote rather that on the real host, and the\n\t# failcounts are thus associated to the pcmk remote. Replace\n\t# the host's name with the pcmk remote's name.\n        remotehost=$(crm_mon --as-xml | xmllint --xpath \"string(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource/node[@name='${HOST}']/../../resource[@resource_agent='${OCF}:pacemaker:remote']/@id)\" -)\n\tif [ -n \"${remotehost}\" ]; then\n\t    crm_failcount -q -G -r $NAME -N $remotehost | grep -q -w INFINITY\n\t    return $?\n\tfi\n\t# If no pcmk remote is currently running, the failcount from\n\t# the ocf resource is useless, compute the failcount from the\n\t# bundle case instead (computed below).\n    fi\n\n    # for bundles, pacemaker can run any bundle replica locally\n    # (e.g. galera-bundle-docker-{0,1,2}), and a failure happens when\n    # there are no more replica to try.\n    # That is, when _at least_ one replica failed locally, and all the\n    # others either failed or are currently running elsewhere.\n    failures=$(bundle_failures_locally $HOST | grep -c -w INFINITY)\n    running=$(bundle_running_globally)\n    test $failures -gt 0 && \\\n    test $(( $failures + $running )) -ge $BUNDLE_REPLICAS\n}\n\ndid_resource_failed_globally() {\n    local remotecount\n    local failures\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# we check the state of an ocf resource only if the\n\t# pcmkremotes are started\n        remotecount=$(crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node)\" -)\n\tif [ \"${remotecount}\" = \"0\" ]; then\n\t    # no pcmkremote is running, so check the bundle state\n\t    # instead of checking the ocf resource\n\t    # bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\t    failures=$(bundle_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\telse\n\t    # ocf resource failed if it failed to start on\n\t    # all $BUNDLE_REPLICAS bundle nodes\n\t    failures=$(ocf_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\tfi\n    else\n\t# bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\tfailures=$(bundle_failures_globally | grep -c -w INFINITY)\n\ttest $failures -eq $BUNDLE_REPLICAS\n    fi\n}\n\n\n# Input validation\n#\n\nNAME=$1\nif [ -z \"${NAME}\" ]; then\n    echo 2>&1 \"Error: argument NAME must not be empty\"\n    exit 1\nfi\n\nBUNDLE_NAME=$2\nif [ -z \"${BUNDLE_NAME}\" ]; then\n    echo 2>&1 \"Error: argument BUNDLE_NAME must not be empty\"\n    exit 1\nfi\n\nROLE_LOCAL=$3\nif [ \"${ROLE_LOCAL}\" = \"_\" ]; then\n    ROLE_LOCAL=\"\"\nfi\n\nROLE_ANYWHERE=$4\nif [ \"${ROLE_ANYWHERE}\" = \"_\" ]; then\n    ROLE_ANYWHERE=\"\"\nfi\n\nif [ -z \"${ROLE_LOCAL}\" ]; then\n    if [ -z \"${ROLE_ANYWHERE}\" ]; then\n        echo 2>&1 \"Error: either ROLE_LOCAL or ROLE_ANYWHERE must be non empty\"\n        exit 1\n    fi\nelse\n    if !(echo \"${ROLE_LOCAL}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n        echo 2>&1 \"Error: argument ROLE_LOCAL must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n        exit 1\n    fi\nfi\n\nif [ -n \"${ROLE_ANYWHERE}\" ] && !(echo \"${ROLE_ANYWHERE}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n    echo 2>&1 \"Error: argument ROLE_ANYWHERE must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n    exit 1\nfi\n\n# Ensure compatibility with pacemaker 2.1\nif pacemaker_supports_promoted; then\n    ROLE_LOCAL=$(echo \"$ROLE_LOCAL\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    ROLE_ANYWHERE=$(echo \"$ROLE_ANYWHERE\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    OCF=\"ocf\"\nelse\n    OCF=\"ocf:\"\nfi\n\nHOST=${5:-$(facter hostname)}\nTIMEOUT=${6:-600}\n\n\n# Configure the search\n# ----\n# Note: we can't use crm_resource in all searches because we can't\n# easily extract the host the OCF resources run on (crm_resource\n# returns the pcmk-remote nodes rather than the hosts)\n# So instead, we implement various searches with XPath directly.\n\nif [ \"${BUNDLE_NAME}\" != \"${NAME}\" ]; then\n# ocf resource\nlocal_resource_xpath=\"//bundle/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node[@name='${HOST}']/../../resource[@id='${NAME}']\"\nany_resource_xpath=\"//bundle//resource[@id='${NAME}']\"\nreplicas_xpath=\"//bundle/primitive[@id='${BUNDLE_NAME}']/../*[boolean(@image) and boolean(@replicas)]\"\nelse\n# bundle resource\nlocal_resource_xpath=\"//bundle[@id='${NAME}']/replica/resource/node[@name='${HOST}']/../../resource\"\nany_resource_xpath=\"//bundle[@id='${NAME}']//resource\"\nreplicas_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nfi\n\nbundle_def_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nBUNDLE_CONTAINER_ENGINE=$(cibadmin -Q | xmllint --xpath \"name(${bundle_def_xpath})\" -)\nBUNDLE_REPLICAS=$(cibadmin -Q | xmllint --xpath \"string(${bundle_def_xpath}/@replicas)\" -)\n\n\n# The wait algorithm follows a two-stage approach\n#  1. Depending on how the script is called, we first check whether\n#     the resource is restarted locally. An A/P resource may be\n#     restarted elsewhere in the cluster.\n#  2. If needed, check whether the A/P resource has restarted\n#     elsewhere. For A/P M/S resources, in case the resource is\n#     restarted as Slave locally, ensure a Master is available.\n\nsuccess=1\nbailout=1\ntimeout=$TIMEOUT\nrole=\"\"\n\n# Stage 1: local check\nif [ -n \"$ROLE_LOCAL\" ]; then\n    log \"Waiting until ${NAME} has restarted on ${HOST} and is in state ${ROLE_LOCAL}\"\n    log \"Will probe resource state with the following XPath pattern: ${local_resource_xpath}\"\n\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resource=$(crm_mon -r --as-xml | xmllint --xpath \"${local_resource_xpath}\" - 2>/dev/null)\n        role=$(echo \"${resource}\" | sed -ne 's/.*\\Wrole=\"\\([^\"]*\\)\".*/\\1/p')\n\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resource}\" | grep -q -w \"\\Wblocked=\\\"true\\\"\"; then\n            log \"${NAME} is blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_locally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif [ \"$role\" = \"$ROLE_LOCAL\" ]; then\n            success=0\n            continue\n        elif [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" = \"$ROLE_ANYWHERE\" ]; then\n            # A/P: we are restarted in the expected state\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_LOCAL} on ${HOST}\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\n# Stage 2: global check\nif [ $timeout -gt 0 ] && [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" != \"$ROLE_ANYWHERE\" ]; then\n    log \"Wait until ${NAME} is restarted anywhere in the cluster in state ${ROLE_ANYWHERE}\"\n    log \"Will probe resource state with the following XPath pattern: ${any_resource_xpath}\"\n\n    success=1\n    bailout=1\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resources=$(crm_mon -r --as-xml | xmllint --xpath \"${any_resource_xpath}\" - 2>/dev/null)\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif ! (echo \"${resources}\" | grep -q -w \"\\Wblocked=\\\"false\\\"\"); then\n            log \"${NAME} blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_globally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resources}\" | grep -q -w \"\\Wrole=\\\"${ROLE_ANYWHERE}\\\"\"; then\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_ANYWHERE} anywhere in the cluster\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\nif [ $timeout -le 0 ]; then\n    log \"Timeout reached after ${TIMEOUT}s while waiting for ${NAME} to be restarted\"\nelif [ $bailout -le 0 ]; then\n    log \"Restart monitoring for ${NAME} cancelled\"\nfi\n\nif [ $success -eq 0 ]; then\n    log \"${NAME} successfully restarted\"\nelse\n    log \"${NAME} was not restarted properly\"\nfi\n\n# Don't block minor update or stack update if the wait was unsuccessful\nexit 0\n"}, "wait-port-and-run.sh": {"mode": "0755", "content": "#!/usr/bin/bash\nset -eu\n\nHOST=$1\nPORT=$2\n\necho \"$(date -u): Checking whether we can bind to ${HOST}:${PORT}\"\nwhile (ss -Htnl src \"${HOST}\" \"sport = :${PORT}\" | grep -wq \"${PORT}\"); do\n    echo \"$(date -u): ${HOST}:${PORT} still in use, waiting...\";\n    sleep 10;\ndone\n\nshift 2\nCOMMAND=\"$*\"\nif [ -z \"${COMMAND}\" ]; then\n    COMMAND=\"true\"\nfi\nexec $COMMAND\n"}, "pacemaker_resource_lock.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nMAX_RETRIES=10\nCIB_ENOTFOUND=105\n\nusage() {\n   echo \"Set a global property in the cluster with a validity timestamp.\"\n   echo \"Usage:\"\n   echo \"   $0 --acquire <lock_name> <lock_owner> <lock_ttl_in_seconds>\"\n   echo \"   $0 --release <lock_name> <lock_owner>\"\n   echo\n}\n\nlog() {\n    echo \"$(date -u): $1\" 1>&2\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\nlock_create() {\n    local name=$1\n    local data=$2\n    # cibadmin won't overwrite a key if someone else succeeded to create it concurrently\n    cibadmin --sync-call --scope crm_config --create --xml-text \"<cluster_property_set id='${name}'><nvpair id='${name}-pair' name='${name}' value='${data}'/></cluster_property_set>\" &>/dev/null\n    return $?\n}\n\nlock_update() {\n    local name=$1\n    local expected_data=$2\n    local new_data=$3\n    # we only update the lock we expect to see, so we can't update someone else's lock\n    cibadmin --sync-call --scope crm_config --modify --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" --xml-text \"<nvpair id='${name}-pair' name='${name}' value='${new_data}'/>\" &>/dev/null\n    return $?\n}\n\nlock_delete() {\n    local name=$1\n    local expected_data=$2\n    # we only delete the lock we expect to see, so we can't delete someone else's lock\n    cibadmin --sync-call --scope crm_config --delete --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" &>/dev/null\n    return $?\n}\n\nlock_get() {\n    local lockname=$1\n    local res\n    local rc\n    res=$(cibadmin --query --scope crm_config --xpath \"//cluster_property_set/nvpair[@name='$lockname']\" 2>/dev/null)\n    rc=$?\n    if [ $rc -eq 0 ]; then\n        echo \"$res\" | sed -n 's/.*value=\"\\([^\"]*\\)\".*/\\1/p'\n    fi\n    return $rc\n}\n\nlock_owner() {\n    local lock=$1\n    echo \"$lock\" | cut -d':' -f1\n}\n\nlock_has_expired() {\n    local lock=$1\n    local expiry=$(echo \"$lock\" | cut -d':' -f2)\n    local now=$(date +%s)\n    test $now -ge $expiry\n}\n\n\n# Perform a lock action and restart if the CIB has been modified before\n# committing the lock action\ntry_action() {\n    local fun=$1\n    local lock=$2\n    local requester=$3\n    local args=${4:-}\n    local tries=$MAX_RETRIES\n    local rc=1\n    if [ \"$fun\" = \"lock_acquire\" ] || [ \"$fun\" = \"lock_release\" ]; then\n        log \"Try running $fun\"\n    else\n        return 2\n    fi\n    while [ $rc -ne 0 ]; do\n        $fun $lock $requester $args\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Operation $1 succeeded\"\n            return 0\n        elif [ $rc -eq 3 ]; then\n            # rc == 3 -> CIB changed before push\n            if [ $tries -eq 0 ]; then\n                log \"Failed to commit after $MAX_RETRIES retries. Bailing out.\"\n                return 2\n            else\n                log \"Failed to commit. Retrying operation.\"\n                tries=$(($tries - 1))\n            fi\n        elif [ $rc -eq 2 ]; then\n            # rc == 2 -> unrecoverable cib error (e.g. pacemaker down)\n            log \"Unexpected failure. Bailing out\"\n            return $rc\n        else\n            # rc == 1 -> lock error (not owner, lock doesn't exists)\n            return $rc\n        fi\n    done\n}\n\n# The lock mechanism uses cibadmin's atomic creation so cluster-wide\n# state coherency is guaranteed by pacemaker\nlock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc\n    local lock\n    local expiry\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -n \"$lock\" ]; then\n        lock_has_expired $lock\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Lock has expired, now available for being held\"\n        else\n            # lock is still held. check whether we're the owner\n            owner=$(lock_owner $lock)\n            if [ \"$owner\" = \"$requester\" ];then\n                log \"Requester already owns the lock, acquiring attempt will just reconfigure the TTL\"\n            else\n                log \"Lock is held by someone else ($owner)\"\n                return 1\n            fi\n        fi\n    else\n        log \"Lock is not held yet\"\n    fi\n\n    # prepare the lock info\n    expiry=$(($(date +%s) + $ttl))\n\n    if [ -n \"$lock\" ]; then\n        log \"Attempting to update the lock\"\n        lock_update $lockname \"$lock\" \"$requester:$expiry\"\n        rc=$?\n    else\n        log \"Attempting to acquire the lock\"\n        lock_create $lockname \"$requester:$expiry\"\n        rc=$?\n    fi\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' acquired by '$requester', valid until $(date -d @$expiry)\"\n        return 0\n    else\n        log \"CIB changed, lock cannot be acquired\"\n        return 3\n    fi\n}\n\n\n# The lock mechanism uses the CIB's num_updates tag to implement\n# a conditional store. Cluster-wide locking is guaranteed by pacemaker\nlock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n    local lock\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        log \"Lock doesn't exist. Nothing to release\"\n        return 0\n    else\n        log \"Lock exists, check whether we're the owner\"\n        owner=$(lock_owner $lock)\n        if [ \"$owner\" != \"$requester\" ];then\n            log \"Lock is held by someone else ($owner), will not unlock\"\n            return 1\n        fi\n    fi\n\n    lock_delete $lockname \"$lock\"\n    rc=$?\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' released by '$requester'\"\n        return 0\n    else\n        log \"CIB deletion error, lock cannot be released\"\n        return 3\n    fi\n}\n\n\n# Retrieve the owner of a lock from the CIB\n# this is a read-only operation, so no need to log debug info\nlock_get_owner() {\n    local lockname=$1\n    local rc\n    local lock\n    local owner\n\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        return 1\n    else\n        lock_owner $lock\n        return 0\n    fi\n}\n\n\nACTION=$1\nLOCKNAME=$2\nREQUESTER=$3\nTTL=${4:-60}\n\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nif [ $ACTION != \"--help\" ]; then\n    if [ -z \"$LOCKNAME\" ]; then\n        error \"You must specific a lock name\"\n    fi\n    if [ $ACTION != \"--owner\" ] && [ $ACTION != \"-o\" ]; then\n        if [ -z \"$REQUESTER\" ]; then\n            error \"You must specific a lock requester\"\n        fi\n    fi\nfi\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) try_action lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --release|-r) try_action lock_release $LOCKNAME $REQUESTER;;\n    --acquire-once|-A) lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --owner|-o) lock_get_owner $LOCKNAME;;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "pacemaker_mutex_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_restart_bundle.sh --lock mysql galera galera-bundle Master _\n# pacemaker_mutex_restart_bundle.sh --lock ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\n\nset -u\n\nusage() {\n    echo \"Restart a clustered resource in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --lock <tripleo-service> <pcmk-resource> <pcmk-bundle> <target-state-local> <target-state-cluster>\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\nACTION=$1\ncase $ACTION in\n    --help) usage; exit 0;;\n    --lock) ;;\n    *) error \"Unknown action '$ACTION'\";;\nesac\n\nTRIPLEO_SERVICE=$2\nLOCK_NAME=${TRIPLEO_SERVICE}-restart-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to restart resource $TRIPLEO_SERVICE\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\nRESOURCE_NAME=$3\nBUNDLE_NAME=$4\nWAIT_TARGET_LOCAL=$5\nWAIT_TARGET_ANYWHERE=${6:-_}\n\nif pacemaker_supports_promoted; then\n    WAIT_TARGET_LOCAL=$(echo \"$5\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    WAIT_TARGET_ANYWHERE=$(echo \"${6:-_}\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    promoted_role=\"Promoted\"\nelse\n    promoted_role=\"Master\"\nfi\n\n# The lock TTL should accomodate for the resource start/promote timeout\nif [ \"$RESOURCE_NAME\" != \"$BUNDLE_NAME\" ]; then\n    if [ \"$WAIT_TARGET_LOCAL\" = \"$promoted_role\" ] || [ \"$WAIT_TARGET_ANYWHERE\" = \"$promoted_role\" ]; then\n        rsc_op=\"promote\"\n    else\n        rsc_op=\"start\"\n    fi\n    # <op id=\"galera-promote-interval-0s\" interval=\"0s\" name=\"promote\" on-fail=\"block\" timeout=\"300s\"/>\n    PCMK_TTL=$(cibadmin -Q | xmllint -xpath \"string(//primitive[@id='${RESOURCE_NAME}']/operations/op[@name='${rsc_op}']/@timeout)\" - | sed 's/s$//')\n    LOCK_TTL=$((PCMK_TTL + 30))\nelse\n    # The podman RA's default start timeout\n    LOCK_TTL=90\nfi\n\nlog \"Acquire a ${LOCK_TTL}s restart lock for service $TRIPLEO_SERVICE before restarting it\"\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nrc=1\nwhile [ $rc -ne 0 ]; do\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire $LOCK_NAME $LOCK_OWNER $LOCK_TTL\n    rc=$?\n    if [ $rc != 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not acquire lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Could not acquire lock, retrying\"\n            sleep 10\n        fi\n    fi\ndone\n\nlog \"Restart the service $TRIPLEO_SERVICE locally\"\n# Reuse the local restart script in t-h-t (driven by env var TRIPLEO_MINOR_UPDATE)\nTRIPLEO_MINOR_UPDATE=true /var/lib/container-config-scripts/pacemaker_restart_bundle.sh $TRIPLEO_SERVICE $RESOURCE_NAME $BUNDLE_NAME $WAIT_TARGET_LOCAL $WAIT_TARGET_ANYWHERE\n\n# If we reached this point, always try to release the lock\nlog \"Release the restart lock for service $TRIPLEO_SERVICE\"\n/var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $LOCK_NAME $LOCK_OWNER\nrc=$?\nif [ $rc -ne 0 ] && [ $rc -ne 1 ]; then\n    error \"Could not release held lock (rc: $rc)\"\nfi\n"}, "pacemaker_mutex_shutdown.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_shutdown.sh --acquire\n# pacemaker_mutex_shutdown.sh --release\n\nset -u\n\nusage() {\n    echo \"Shutdown a cluster node in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --acquire # prevent other node from shutting down until we hold the lock\"\n    echo \"   $0 --release # release the lock, other node can compete for the shutdown lock\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nshutdown_lock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc=1\n    local current_owner\n    local owner_stopped\n    local owner_rc\n\n    log \"Acquiring the shutdown lock\"\n    while [ $rc -ne 0 ]; do\n        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire-once $lockname $requester $ttl\n        rc=$?\n        if [ $rc -ne 0 ]; then\n            if [ $rc -eq 2 ]; then\n                error \"Could not acquire the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n            else\n                # The lock is held by another node.\n                current_owner=$(/var/lib/container-config-scripts/pacemaker_resource_lock.sh --owner $lockname)\n                owner_rc=$?\n                if [ $owner_rc -eq 2 ]; then\n                    error \"Could not get the shutdown lock owner due to unrecoverable error (rc: $owner_rc), bailing out\"\n                fi\n                if [ $owner_rc -eq 0 ]; then\n                    # If the owner is marked as offline, that means it has shutdown and\n                    # we can clean the lock preemptively and try to acquire it.\n                    owner_stopped=$(crm_mon -1X | xmllint --xpath 'count(//nodes/node[@name=\"'${current_owner}'\" and @online=\"false\" and @unclean=\"false\"])' -)\n                    if [ \"${owner_stopped}\" = \"1\" ]; then\n                        log \"Shutdown lock held by stopped node '${current_owner}', lock can be released\"\n                        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $current_owner\n                        continue\n                    fi\n                fi\n                log \"Shutdown lock held by another node (rc: $rc), retrying\"\n                sleep 10\n            fi\n        fi\n    done\n    log \"Shutdown lock acquired\"\n    return 0\n}\n\n\n# Release the lock if we still own it. Not owning it anymore is not fatal\nshutdown_lock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n\n    log \"Releasing the shutdown lock\"\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $requester\n    rc=$?\n    if [ $rc -ne 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not release the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Shutdown lock no longer held, nothing to do\"\n        fi\n    else\n        log \"Shutdown lock released\"\n    fi\n    return 0\n}\n\n\nACTION=$1\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nLOCK_NAME=tripleo-shutdown-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to aquire the shutdown lock\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\n# We start with a very high TTL, that long enough to accomodate a cluster stop.\n# As soon as the node will get offline, the other competing node will be entitled\n# to steal the lock, so they should never wait that long in practice.\nLOCK_TTL=600\n\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) shutdown_lock_acquire ${LOCK_NAME} ${LOCK_OWNER} ${LOCK_TTL};;\n    --release|-r) shutdown_lock_release ${LOCK_NAME} ${LOCK_OWNER};;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "nova_statedir_ownership.py": {"mode": "0700", "content": "#!/usr/bin/python3\n#\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\nimport logging\nimport os\nimport pwd\nimport selinux\nimport stat\nimport sys\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_statedir')\n\n\nclass PathManager(object):\n    \"\"\"Helper class to manipulate ownership of a given path\"\"\"\n    def __init__(self, path):\n        self.path = path\n        self.uid = None\n        self.gid = None\n        self.is_dir = None\n        self.secontext = None\n        self._update()\n\n    def _update(self):\n        try:\n            statinfo = os.stat(self.path)\n            self.is_dir = stat.S_ISDIR(statinfo.st_mode)\n            self.uid = statinfo.st_uid\n            self.gid = statinfo.st_gid\n            self.secontext = selinux.lgetfilecon(self.path)[1]\n        except Exception:\n            LOG.exception('Could not update metadata for %s', self.path)\n            raise\n\n    def __str__(self):\n        return \"uid: {} gid: {} path: {}{}\".format(\n            self.uid,\n            self.gid,\n            self.path,\n            '/' if self.is_dir else ''\n        )\n\n    def has_owner(self, uid, gid):\n        return self.uid == uid and self.gid == gid\n\n    def has_either(self, uid, gid):\n        return self.uid == uid or self.gid == gid\n\n    def chown(self, uid, gid):\n        target_uid = -1\n        target_gid = -1\n        if self.uid != uid:\n            target_uid = uid\n        if self.gid != gid:\n            target_gid = gid\n        if (target_uid, target_gid) != (-1, -1):\n            LOG.info('Changing ownership of %s from %d:%d to %d:%d',\n                     self.path,\n                     self.uid,\n                     self.gid,\n                     self.uid if target_uid == -1 else target_uid,\n                     self.gid if target_gid == -1 else target_gid)\n            try:\n                os.chown(self.path, target_uid, target_gid)\n                self._update()\n            except Exception:\n                LOG.exception('Could not change ownership of %s: ',\n                              self.path)\n                raise\n        else:\n            LOG.info('Ownership of %s already %d:%d',\n                     self.path,\n                     uid,\n                     gid)\n\n    def chcon(self, context):\n        # If dir returns whether to recusively set context\n        try:\n            try:\n                selinux.lsetfilecon(self.path, context)\n                LOG.info('Setting selinux context of %s to %s',\n                     self.path, context)\n                return True\n            except OSError as e:\n                if self.is_dir and e.errno == 95:\n                    # Operation not supported, assume NFS mount and skip\n                    LOG.info('Setting selinux context not supported for %s',\n                             self.path)\n                    return False\n                else:\n                    raise\n        except Exception:\n            LOG.exception('Could not set selinux context of %s to %s:',\n                          self.path, context)\n            raise\n\n\nclass NovaStatedirOwnershipManager(object):\n    \"\"\"Class to manipulate the ownership of the nova statedir (/var/lib/nova).\n\n       The nova uid/gid differ on the host and container images. An upgrade\n       that switches from host systemd services to docker requires a change in\n       ownership. Previously this was a naive recursive chown, however this\n       causes issues if nova instance are shared via an NFS mount: any open\n       filehandles in qemu/libvirt fail with an I/O error (LP1778465).\n\n       Instead the upgrade/FFU ansible tasks now lay down a marker file when\n       stopping and disabling the host systemd services. We use this file to\n       determine the host nova uid/gid. We then walk the tree and update any\n       files that have the host uid/gid to the docker nova uid/gid. As files\n       owned by root/qemu etc... are ignored this avoids the issues with open\n       filehandles. The marker is removed once the tree has been walked.\n\n       For subsequent runs, or for a new deployment, we simply ensure that the\n       docker nova user/group owns all directories. This is required as the\n       directories are created with root ownership in host_prep_tasks (the\n       docker nova uid/gid is not known in this context).\n    \"\"\"\n    def __init__(self, statedir, upgrade_marker='upgrade_marker',\n                 nova_user='nova', secontext_marker='../_nova_secontext',\n                 exclude_paths=None):\n        self.statedir = statedir\n        self.nova_user = nova_user\n\n        self.upgrade_marker_path = os.path.join(statedir, upgrade_marker)\n        self.secontext_marker_path = os.path.normpath(os.path.join(statedir, secontext_marker))\n        self.upgrade = os.path.exists(self.upgrade_marker_path)\n\n        self.exclude_paths = [self.upgrade_marker_path]\n        if exclude_paths is not None:\n            for p in exclude_paths:\n                if not p.startswith(os.path.sep):\n                    p = os.path.join(self.statedir, p)\n                self.exclude_paths.append(p)\n\n        self.target_uid, self.target_gid = self._get_nova_ids()\n        self.previous_uid, self.previous_gid = self._get_previous_nova_ids()\n        self.id_change = (self.target_uid, self.target_gid) != \\\n            (self.previous_uid, self.previous_gid)\n        self.target_secontext = self._get_secontext()\n\n    def _get_nova_ids(self):\n        nova_uid, nova_gid = pwd.getpwnam(self.nova_user)[2:4]\n        return nova_uid, nova_gid\n\n    def _get_previous_nova_ids(self):\n        if self.upgrade:\n            statinfo = os.stat(self.upgrade_marker_path)\n            return statinfo.st_uid, statinfo.st_gid\n        else:\n            return self._get_nova_ids()\n\n    def _get_secontext(self):\n        if os.path.exists(self.secontext_marker_path):\n            return selinux.lgetfilecon(self.secontext_marker_path)[1]\n        else:\n            return None\n\n    def _walk(self, top, chcon=True):\n        for f in os.listdir(top):\n            pathname = os.path.join(top, f)\n\n            if pathname in self.exclude_paths:\n                continue\n\n            try:\n                pathinfo = PathManager(pathname)\n                LOG.info(\"Checking %s\", pathinfo)\n                if pathinfo.is_dir:\n                    # Always chown the directories\n                    pathinfo.chown(self.target_uid, self.target_gid)\n                    chcon_r = chcon\n                    if chcon:\n                        chcon_r = pathinfo.chcon(self.target_secontext)\n                    self._walk(pathname, chcon_r)\n                elif self.id_change:\n                    # Only chown files if it's an upgrade and the file is owned by\n                    # the host nova uid/gid\n                    pathinfo.chown(\n                        self.target_uid if pathinfo.uid == self.previous_uid\n                        else pathinfo.uid,\n                        self.target_gid if pathinfo.gid == self.previous_gid\n                        else pathinfo.gid\n                    )\n                    if chcon:\n                        pathinfo.chcon(self.target_secontext)\n            except Exception:\n                # Likely to have been caused by external systems\n                # interacting with this directory tree,\n                # especially on NFS e.g snapshot dirs.\n                # Just ignore it and continue on to the next entry\n                continue\n\n    def run(self):\n        LOG.info('Applying nova statedir ownership')\n        LOG.info('Target ownership for %s: %d:%d',\n                 self.statedir,\n                 self.target_uid,\n                 self.target_gid)\n\n        pathinfo = PathManager(self.statedir)\n        LOG.info(\"Checking %s\", pathinfo)\n        pathinfo.chown(self.target_uid, self.target_gid)\n        chcon = self.target_secontext is not None\n\n        if chcon:\n            pathinfo.chcon(self.target_secontext)\n\n        self._walk(self.statedir, chcon)\n\n        if self.upgrade:\n            LOG.info('Removing upgrade_marker %s',\n                     self.upgrade_marker_path)\n            os.unlink(self.upgrade_marker_path)\n\n        LOG.info('Nova statedir ownership complete')\n\n\ndef get_exclude_paths():\n    exclude_paths = os.environ.get('NOVA_STATEDIR_OWNERSHIP_SKIP')\n    if exclude_paths is not None:\n        exclude_paths = exclude_paths.split(os.pathsep)\n    return exclude_paths\n\n\nif __name__ == '__main__':\n    NovaStatedirOwnershipManager('/var/lib/nova', exclude_paths=get_exclude_paths()).run()\n"}, "nova_wait_for_compute_service.py": {"mode": "0755", "content": "#!/usr/bin/python3\n#\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\nimport argparse\nimport logging\nimport os\nimport six\nimport socket\nimport sys\nimport time\n\n\nfrom keystoneauth1 import loading\nfrom keystoneauth1 import session\n\nfrom novaclient import client\n\n# In python3 SafeConfigParser was renamed to ConfigParser and the default\n# for duplicate options default to true. In case of nova it is valid to\n# have duplicate option lines, e.g. passthrough_whitelist which leads to\n# issues reading the nova.conf\n# https://bugs.launchpad.net/tripleo/+bug/1827775\nif six.PY3:\n    from six.moves.configparser import ConfigParser\n    config = ConfigParser(strict=False)\nelse:\n    from six.moves.configparser import SafeConfigParser\n    config = SafeConfigParser()\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_wait_for_compute_service')\n\niterations = 60\ntimeout = 10\nnova_cfg = '/etc/nova/nova.conf'\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(usage='%(prog)s [options]')\n    parser.add_argument('-k', '--insecure',\n                        action=\"store_false\",\n                        dest='insecure',\n                        default=True,\n                        help='Allow insecure connection when using SSL')\n\n    args = parser.parse_args()\n    LOG.debug('Running with parameter insecure = %s',\n              args.insecure)\n\n    if os.path.isfile(nova_cfg):\n        try:\n            config.read(nova_cfg)\n        except Exception:\n            LOG.exception('Error while reading nova.conf:')\n    else:\n        LOG.error('Nova configuration file %s does not exist', nova_cfg)\n        sys.exit(1)\n\n    my_host = config.get('DEFAULT', 'host')\n    if not my_host:\n        # If host isn't set nova defaults to this\n        my_host = socket.gethostname()\n\n    loader = loading.get_plugin_loader('password')\n    auth = loader.load_from_options(\n        auth_url=config.get('neutron',\n                            'auth_url'),\n        username=config.get('neutron',\n                            'username'),\n        password=config.get('neutron',\n                            'password'),\n        project_name=config.get('neutron',\n                                'project_name'),\n        project_domain_name=config.get('neutron',\n                                       'project_domain_name'),\n        user_domain_name=config.get('neutron',\n                                    'user_domain_name'))\n    sess = session.Session(auth=auth, verify=args.insecure)\n    nova = client.Client('2.11', session=sess, endpoint_type='internal',\n                         region_name=config.get('neutron', 'region_name'))\n\n    # Wait until this host is listed in the service list\n    for i in range(iterations):\n        try:\n            service_list = nova.services.list(binary='nova-compute')\n            for entry in service_list:\n                host = getattr(entry, 'host', '')\n                zone = getattr(entry, 'zone', '')\n                if host == my_host and zone != 'internal':\n                    LOG.info('Nova-compute service registered')\n                    sys.exit(0)\n            LOG.info('Waiting for nova-compute service to register')\n        except Exception:\n            LOG.exception(\n                'Error while waiting for nova-compute service to register')\n        time.sleep(timeout)\nsys.exit(1)\n\n# vim: set et ts=4 sw=4 :\n"}}, "tuple:('role_data', 'docker_config')": {"step_2": {"nova_compute_init_log": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "step_3": {"nova_statedir_owner": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "net": "none", "user": "root", "security_opt": ["label=disable"], "privileged": false, "detach": false, "volumes": ["/var/lib/nova:/var/lib/nova:shared", "/var/lib/_nova_secontext:/var/lib/_nova_secontext:shared,z", "/var/lib/container-config-scripts:/container-config-scripts:z"], "command": "/container-config-scripts/pyshim.sh /container-config-scripts/nova_statedir_ownership.py", "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725", "__OS_DEBUG": "true", "NOVA_STATEDIR_OWNERSHIP_SKIP": "triliovault-mounts"}}}, "step_5": {"nova_compute": {"start_order": 3, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "ulimit": ["nofile=131072", "memlock=67108864"], "ipc": "host", "net": "host", "privileged": true, "user": "nova", "restart": "always", "depends_on": ["tripleo_nova_libvirt.target"], "healthcheck": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova", "/etc/ssh/ssh_known_hosts:/etc/ssh/ssh_known_hosts:ro", "/var/lib/kolla/config_files/nova_compute.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova_libvirt:/var/lib/kolla/config_files/src:ro", "/var/lib/config-data/puppet-generated/iscsid/etc/iscsi:/var/lib/kolla/config_files/src-iscsid:ro", "/var/lib/tripleo-config/ceph:/var/lib/kolla/config_files/src-ceph:z", "/dev:/dev", "/lib/modules:/lib/modules:ro", "/run:/run", "/run/nova:/run/nova:z", "/var/lib/iscsi:/var/lib/iscsi:z", "/var/lib/libvirt:/var/lib/libvirt:shared", "/sys/class/net:/sys/class/net", "/sys/bus/pci:/sys/bus/pci", "/boot:/boot:ro", "/var/lib/nova:/var/lib/nova:shared"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS", "LIBGUESTFS_BACKEND": "direct"}}, "nova_wait_for_compute_service": {"start_order": 4, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "net": "host", "detach": false, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/lib/kolla/config_files/nova_compute_wait_for_compute_service.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova_libvirt:/var/lib/kolla/config_files/src:ro", "/var/log/containers/nova:/var/log/nova", "/var/lib/container-config-scripts:/container-config-scripts"], "user": "nova", "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS", "__OS_DEBUG": "true"}}}}, "tuple:('role_data', 'service_name')": "nova_compute", "role_data": {"service_name": "nova_compute", "monitoring_subscription": "overcloud-nova-compute", "config_settings": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::compute::vcpu_pin_set": [], "nova::compute::cpu_shared_set": [], "nova::compute::cpu_dedicated_set": [], "nova::compute::reserved_host_memory": 512, "nova::compute::reserved_huge_pages": [], "nova::compute::neutron_physnets_numa_nodes_mapping": {}, "nova::compute::neutron_tunnel_numa_nodes": [], "nova::compute::resume_guests_state_on_host_boot": false, "nova::compute::libvirt::rx_queue_size": 512, "nova::compute::libvirt::tx_queue_size": 512, "nova::compute::libvirt::file_backed_memory": 0, "nova::compute::libvirt::volume_use_multipath": false, "nova::compute::libvirt::hw_machine_type": "x86_64=pc-q35-rhel9.0.0", "nova::compute::mkisofs_cmd": "mkisofs", "compute_enable_ksm": false, "nova::compute::rbd::libvirt_images_rbd_pool": "vms", "tripleo::profile::base::nova::compute::nova_nfs_enabled": false, "nfs_backend_enable": false, "nfs_share": "", "nfs_options": "context=system_u:object_r:nfs_t:s0", "nfs_vers": "4.2", "nova::compute::libvirt::num_pcie_ports": 16, "nova::compute::libvirt::mem_stats_period_seconds": 10, "nova::compute::rbd::ephemeral_storage": false, "resume_guests_state_on_host_boot": false, "nova::cpu_allocation_ratio": 0.0, "nova::ram_allocation_ratio": 1.0, "nova::disk_allocation_ratio": 0.0, "nova::compute::max_disk_devices_to_attach": -1, "nova::compute::libvirt::pmem_namespaces": "", "nova_pmem_namespaces": "", "nova::compute::image_cache::remove_unused_original_minimum_age_seconds": 86400, "nova::compute::libvirt::swtpm_enabled": false, "nova::compute::vgpu::vgpu_types_device_addresses_mapping": {}, "compute_disable_irqbalance": false, "nova::compute::libvirt::cpu_mode": "host-model", "nova::compute::libvirt::cpu_models": [], "nova::compute::libvirt::cpu_model_extra_flags": "", "nova_compute_opt_volumes": [], "nova_compute_startup_delay": 0, "nova_compute_opt_env_vars": {}, "nova::workarounds::never_download_image_if_on_rbd": false, "nova::compute::image_type_exclude_list": [], "nova_permit_post_copy": true, "nova_permit_auto_converge": true, "nova_libvirt_max_queues": 0, "nova_glance_enable_rbd_download": false, "nova_glance_rbd_download_multistore_id": "", "nova::compute::force_raw_images": true, "nova::compute::use_cow_images": true, "nova::compute::libvirt::preallocate_images": "none", "nova::compute::image_cache::manager_interval": 2400, "nova::compute::image_cache::remove_unused_base_images": true, "nova::compute::image_cache::remove_unused_resized_minimum_age_seconds": 3600, "nova::compute::image_cache::precache_concurrency": 1, "nova::compute::provider::custom_inventories": {}, "nova::compute::rbd::libvirt_images_rbd_glance_copy_poll_interval": 15, "nova::compute::rbd::libvirt_images_rbd_glance_copy_timeout": 600, "ContainerNovaComputeImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "ContainerNovaLibvirtConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-libvirt:17.1", "nova::compute::libvirt::manage_libvirt_services": false, "nova::compute::pci::passthrough": "[{\"devname\": \"dummy-dev\", \"physical_network\": \"dummy_sriov_net\"}]", "nova::compute::libvirt::migration_support": false, "tripleo::profile::base::nova::migration::client::nova_compute_enabled": true, "tripleo::profile::base::nova::migration::client::ssh_private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtEHfMqe/CUfsijQpoJHgkGtXu7olMqGTlbiR3w/aOmycSlLy\nDRC1GFuaSXaRmZzDEoClHGQeMcc2eNzi/edgcjd0Zn9zUnCmq7wbcDYgInwnNgcw\na7jw/sCw7FgfL5rjJus0dGSwcG+aaStUsjcF0f0KwTCnMYPOBQmoDmfcLM/KXwEg\n6xPvoUeXWLmuiYUhX8Ov6/pzQ8qhjwh5QxMGSmDqkE9k+g1AHUAVCnx1H7ABfLgW\nDg5nDt+z1POuw62ttzTjIiTJkOPGzyTgPSUM8G+zcvVUeg6R0tsUj5SdIdpx6ldR\nibjRUo6d+bOyBnK/CLKaXVW0wFMUlqf5vOKEHwIDAQABAoIBAA7SJbRoB3SPZdr9\nK/kIVYL6WHXWec1+bICQcun4xzQrjvQrerxpRcKRcY+dwJ31PjLMwgaiErt8CjYI\nz18aAZuShK8SxpweKA4Rwt3IjX7YX275S1loFOSVWlasbi//V7Lw/tidGMa93l/M\nUMPwczmPSS2WPwD6uds/LoK0tm02qy9xv/1v36xGZTClKbGJm7Gr8DqRBfSrF/Xi\ncUWswxbiEfwVlLQ3KUjH8A/nqoOwDFlkmyw4I1t16Nto6fvsywSS80z2+PZUZyN1\nwcb+Kw7UiJhSiM3E58ecFKloLb0pnuO4EIDqIP3+uoOrvjM4hUHGtDpjcclz7fY8\ntzVi3OECgYEAzz+tx3/tMFbuSjivurgoXFVDDIOAZG3vWY2oeA30j22uUhfXnS4Q\npHRLfxh01avP21fb/N8dlipl/ZY97OsdMghLa8LVYKKs+dpJObiO9COXqOCgf9uH\nOuA8HUiet1vrO8x000zlVRl08jZEhHd87qcgA8jXFhzVQYkuk2aB4D8CgYEA3qjL\ndECE1S9QLmivvcRs+512yPaiXjB6EpVABiSPha2JrFh3vjWzItMb1dQEa3w12kyg\nZpVIfUvjkdaVAU0jJRTX2LivQw1Eb+TIBKTxN0bpsbzGLhZV22y5gqTqlWVtalwr\nsXoCyNMi6TJg3Dm4Jh662f2HI566+NZ5f1J0ZCECgYATYCGwCA61uTrX54rOSw9C\n6pm1HsovGXQKDEtWxf86NYfZlu8Ke9cEePbh7/hkTYbGO+WOM5HBiz8zwQsTDjwR\nhGMAtRtbyHmhdj3oTo/W1UkFedKngW+YGoSkPDjSbZ6FXa0CYCsElLexOU+Yjs84\nRJmioPEy2Qk68kdPcoLr4wKBgDedzgQhwYxGny6Yc51hHR/Ue0FDaddBIJzXrwBZ\nzYfEiykDiBV48F/4f3Fv3hLDGeiHT2GQVGUHubZJ9To3/Flc1XWK3CxRehDBAr02\nPhJioikFUDNe3N9MTyI082uRxDzW/Qr9wQhMxOGe9dbMjBTphB7DVygsk+NoHz0Q\naYZBAoGBAIcWC5mh7BK88xWwH7lTd29gBC0sMfV+4L8/NWQvnxg1omfjfJ32e5sd\n5QqKigi6MtTJwDtXO1EXLxYOjX1zc2jBRYP5NZioxZd0Bc0UvBowcSP5ib5fK4LM\nZzEajZhutEfz1KycMybzQtyzmurMKTVPoLRxIftwg9KKYK7tdeMg\n-----END RSA PRIVATE KEY-----\n", "tripleo::profile::base::nova::migration::client::ssh_port": 2022, "nova::compute::rbd::libvirt_images_rbd_glance_store_name": "default_backend", "nova::compute::rbd::libvirt_images_rbd_ceph_conf": "/etc/ceph/ceph.conf", "nova::compute::rbd::libvirt_rbd_user": "openstack", "nova::compute::rbd::rbd_keyring": "client.openstack", "tripleo::profile::base::nova::compute::cinder_nfs_backend": false, "rbd_persistent_storage": false, "rbd_disk_cachemodes": ["network=writeback"], "nova::cinder::username": "cinder", "nova::cinder::auth_type": "v3password", "nova::cinder::project_name": "service", "nova::cinder::password": "FEja3ueKnb89UNaV9R0nCV9rF", "nova::cinder::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::cinder::region_name": "regionOne", "nova::compute::rbd::libvirt_rbd_secret_key": "AQCtBi1qAAAAABAAlMbuGDpAuKfBXDWSJGSRjw==", "nova::compute::rbd::libvirt_rbd_secret_uuid": "f72fc8e7-c471-4878-af38-3eab533fd252", "nova::compute::instance_usage_audit": true, "nova::compute::instance_usage_audit_period": "hour", "nova::compute::consecutive_build_service_disable_threshold": "10", "nova::compute::live_migration_wait_for_vif_plug": true, "nova::migration::libvirt::live_migration_permit_post_copy": true, "nova::migration::libvirt::live_migration_permit_auto_converge": true, "nova::migration::libvirt::live_migration_tunnelled": false, "nova::compute::vncserver_proxyclient_address": "%{hiera('internal_api')}", "nova::compute::vncproxy_host": "standalone.ooo.test", "nova::vncproxy::common::vncproxy_protocol": "https", "nova::vncproxy::common::vncproxy_host": "standalone.ooo.test", "nova::vncproxy::common::vncproxy_port": "13080", "nova::compute::verify_glance_signatures": false, "nova::workarounds::disable_compute_service_check_for_ffu": false}, "service_config_settings": {"rsyslog": {"tripleo_logging_sources_nova_compute": [{"tag": "openstack.nova.compute", "file": "/var/log/containers/nova/nova-compute.log"}]}, "collectd": {"tripleo.collectd.plugins.nova_compute": ["virt"], "collectd::plugin::virt::connection": "qemu:///system"}}, "puppet_config": {"config_volume": "nova_libvirt", "puppet_tags": "nova_config", "step_config": "# TODO(emilien): figure how to deal with libvirt profile.\n# We'll probably treat it like we do with Neutron plugins.\n# Until then, just include it in the default nova-compute role.\ninclude tripleo::profile::base::nova::compute::libvirt\n", "config_image": "registry.redhat.io/rhosp-rhel9/openstack-nova-libvirt:17.1"}, "kolla_config": {"/var/lib/kolla/config_files/nova_compute.json": {"command": "/usr/bin/nova-compute ", "config_files": [{"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src-iscsid/*", "dest": "/etc/iscsi/", "merge": true, "preserve_properties": true}, {"source": "/var/lib/kolla/config_files/src-ceph/", "dest": "/etc/ceph/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}, {"path": "/etc/ceph/ceph.client.openstack.keyring", "owner": "nova:nova", "perm": "0600"}]}, "/var/lib/kolla/config_files/nova_compute_wait_for_compute_service.json": {"command": "/container-config-scripts/pyshim.sh /container-config-scripts/nova_wait_for_compute_service.py", "config_files": [{"source": "/var/lib/kolla/config_files/src/*", "dest": "/", "merge": true, "preserve_properties": true}], "permissions": [{"path": "/var/log/nova", "owner": "nova:nova", "recurse": true}]}}, "container_config_scripts": {"container_puppet_apply.sh": {"mode": "0700", "content": "#!/bin/bash\nset -eux\nSTEP=$1\nTAGS=$2\nCONFIG=$3\nEXTRA_ARGS=${4:-''}\nif [ -d /tmp/puppet-etc ]; then\n  # ignore copy failures as these may be the same file depending on docker mounts\n  cp -a /tmp/puppet-etc/* /etc/puppet || true\nfi\necho \"{\\\"step\\\": ${STEP}}\" > /etc/puppet/hieradata/docker_puppet.json\n# $::deployment_type in puppet-tripleo\nexport FACTER_deployment_type=containers\nset +e\npuppet apply $EXTRA_ARGS \\\n    --verbose \\\n    --detailed-exitcodes \\\n    --summarize \\\n    --color=false \\\n    --modulepath /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules \\\n    --tags $TAGS \\\n    -e \"noop_resource('package'); ${CONFIG}\"\nrc=$?\nset -e\nset +ux\nif [ $rc -eq 2 -o $rc -eq 0 ]; then\n    exit 0\nfi\nexit $rc\n"}, "pyshim.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n#\n# Usage: pyshim.sh <script and/or arguments>\n#\n# Unfortunately THT doesn't know which version of python might be in a\n# container so we need this script to be able to try python3 or python2\n# depending on availability.  Since this is a temporary shim until we've\n# fully cut over to python3, we check for the existence of python3 first\n# before falling back to python2. This will help in the transition from\n# python2 based containers to python3.\n\nshow_usage() {\n    echo \"Usage: pyshim.sh <script and/or arguments>\"\n}\n\nif [ $# -lt 1 ]\nthen\n    show_usage\n    exit 1\nfi\n\nset -x\nif command -v python3 >/dev/null; then\n    python3 \"$@\"\nelif command -v python2 >/dev/null; then\n    python2 \"$@\"\nelif command -v python >/dev/null; then\n    python \"$@\"\nelse\n    echo \"ERROR: python is not available!\"\n    exit 1\nfi\n"}, "pacemaker_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nset -u\n\n# ./pacemaker_restart_bundle.sh mysql galera galera-bundle Master _\n# ./pacemaker_restart_bundle.sh redis redis redis-bundle Slave Master\n# ./pacemaker_restart_bundle.sh ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\nRESTART_SCRIPTS_DIR=$(dirname $0)\nTRIPLEO_SERVICE=$1\nRESOURCE_NAME=$2\nBUNDLE_NAME=$3\nWAIT_TARGET_LOCAL=$4\nWAIT_TARGET_ANYWHERE=${5:-_}\nTRIPLEO_MINOR_UPDATE=\"${TRIPLEO_MINOR_UPDATE:-false}\"\nTRIPLEO_HA_WRAPPER_RESOURCE_EXISTS=\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS:-false}\"\n\nbundle_can_be_restarted() {\n    local bundle=$1\n    # As long as the resource bundle is managed by pacemaker and is\n    # not meant to stay stopped, no matter the state of any inner\n    # pcmk_remote or ocf resource, we should restart it to give it a\n    # chance to read the new config.\n    [ \"$(crm_resource --meta -r $1 -g is-managed 2>/dev/null)\" != \"false\" ] && \\\n    [ \"$(crm_resource --meta -r $1 -g target-role 2>/dev/null)\" != \"Stopped\" ]\n}\n\nlog() {\n    local msg=$1\n    logger -t pcmkrestart \"$1\"\n}\n\nHOSTNAME=$(/bin/hostname -s)\nif [ x\"${TRIPLEO_MINOR_UPDATE,,}\" != x\"true\" ]; then\n    if [ x\"${TRIPLEO_HA_WRAPPER_RESOURCE_EXISTS,,}\" = x\"false\" ]; then\n        # Do not restart during initial deployment, as the resource\n        # has just been created.\n        SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n        if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n            log \"Initial deployment, skipping the restart of ${BUNDLE_NAME}\"\n\tfi\n        exit 0\n    else\n        # During a stack update, this script is called in parallel on\n        # every node the resource runs on, after the service's configs\n        # have been updated on all nodes. So we need to run pcs only\n        # once (e.g. on the service's boostrap node).\n        if bundle_can_be_restarted ${BUNDLE_NAME}; then\n            SERVICE_NODEID=$(/bin/hiera -c /etc/puppet/hiera.yaml \"${TRIPLEO_SERVICE}_short_bootstrap_node_name\")\n            if [[ \"${HOSTNAME,,}\" == \"${SERVICE_NODEID,,}\" ]]; then\n                replicas_running=$(crm_resource -Q -r $BUNDLE_NAME --locate 2>&1 | wc -l)\n                if [ \"$replicas_running\" != \"0\" ]; then\n                    log \"Restarting ${BUNDLE_NAME} globally. Stopping:\"\n                    /sbin/pcs resource disable --wait=600 $BUNDLE_NAME\n                    log \"Restarting ${BUNDLE_NAME} globally. Starting:\"\n                    /sbin/pcs resource enable --wait=600 $BUNDLE_NAME\n                else\n                    log \"${BUNDLE_NAME} is not running anywhere,\" \\\n                         \"cleaning up to restart it globally if necessary\"\n                    /sbin/pcs resource cleanup $BUNDLE_NAME\n                fi\n            else\n                log \"Skipping global restart of ${BUNDLE_NAME} on ${HOSTNAME} it will be restarted by node ${SERVICE_NODEID}\"\n            fi\n\n        else\n            log \"No global restart needed for ${BUNDLE_NAME}.\"\n        fi\n    fi\nelse\n    # During a minor update workflow however, a host gets fully\n    # updated before updating the next one. So unlike stack\n    # update, at the time this script is called, the service's\n    # configs aren't updated on all nodes yet. So only restart the\n    # resource locally, where it's guaranteed that the config is\n    # up to date.\n    HOST=$(facter hostname)\n\n    if bundle_can_be_restarted ${BUNDLE_NAME}; then\n\t# if the resource is running locally, restart it\n\tif crm_resource -r $BUNDLE_NAME --locate 2>&1 | grep -w -q \"${HOST}\"; then\n            log \"Restarting ${BUNDLE_NAME} locally on '${HOST}'\"\n            /sbin/pcs resource restart $BUNDLE_NAME \"${HOST}\"\n\n\telse\n\t    # At this point, if no resource is running locally, it's\n\t    # either because a) it has failed previously, or b) because\n\t    # it's an A/P resource running elsewhere.\n\t    # By cleaning up resource, we ensure that a) it will try to\n\t    # restart, or b) it won't do anything if the resource is\n\t    # already running elsewhere.\n            log \"${BUNDLE_NAME} is currently not running on '${HOST}',\" \\\n                 \"cleaning up its state to restart it if necessary\"\n            /sbin/pcs resource cleanup $BUNDLE_NAME node=\"${HOST}\"\n\tfi\n\n\t# Wait until the resource is in the expected target state\n\t$RESTART_SCRIPTS_DIR/pacemaker_wait_bundle.sh \\\n            $RESOURCE_NAME $BUNDLE_NAME \\\n            \"$WAIT_TARGET_LOCAL\" \"$WAIT_TARGET_ANYWHERE\" \\\n\t    \"${HOST}\" 600\n    else\n        log \"No restart needed for ${BUNDLE_NAME}.\"\n    fi\nfi\n"}, "pacemaker_wait_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# ----\n# Wait for an OCF resource or a bundle to be restarted\n# ----\n# e.g.:\n# M/S OCF:      $0 galera galera-bundle Master\n# clone OCF:    $0 rabbitmq rabbitmq-bundle Started\n# A/P M/S OCF:  $0 redis redis-bundle Slave Master\n# A/P bundle:   $0 openstack-cinder-volume openstack-cinder-volume _ Started\n# clone bundle: $0 haproxy-bundle haproxy-bundle Started\n\n# design note 1:\n#  - this script is called during a minor update; it is called\n#    once per node that hosts a service replica.\n#  - the purpose of this script is to ensure that restarting the\n#    service replica locally won't disrupt the service availability\n#    for the end user. To reach that goal, the script waits until the\n#    service is restarted locally or globallu and reaches a given\n#    target state (i.e. Started, Slave or Master).\n# design note 2:\n#   - we don't want to track restart error: our only job is to ensure\n#     service restart synchronization, not service health.\n#   - In particular, we don't want to error out in case the resource\n#     cannot be restarted locally, because that would make the minor\n#     update fail, even if potentially other replicas still provide\n#     the service.\n# design note 3:\n#   - we can bail out early if we determine that the resource can't\n#     be restarted automatically by pacemaker (e.g. its \"blocked\",\n#     unmanaged or disabled).\n\nlog() {\n    local msg=$1\n    echo \"$(date -u): $1\"\n}\n\nusage() {\n    echo 2>&1 \"Usage: $0 NAME BUNDLE_NAME ROLE_LOCAL [ROLE_ANYWHERE] [HOST] [TIMEOUT]\"\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\n#\n# Utility functions to detect stuck resources\n#\n\nbundle_failures_locally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local replica_name\n    for i in $(seq 0 $last); do\n\treplica_name=${BUNDLE_NAME}-${engine}-${i}\n\tcrm_failcount -q -G -r $replica_name -N $HOST\n    done\n}\n\nbundle_failures_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    for i in $(seq 0 $last); do\n\tcrm_failcount -q -G -r ${BUNDLE_NAME}-${engine}-${i}\n    done\n}\n\nbundle_running_globally() {\n    local engine=$BUNDLE_CONTAINER_ENGINE\n    # return the number of running bundles replica, i.e. the number of\n    # docker/podman resource replicas currently running in the cluster\n    crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:heartbeat:${engine}']/node)\" -\n}\n\nocf_failures_globally() {\n    local replicas=$BUNDLE_REPLICAS\n    local last=$(($replicas - 1))\n    local bundle_node\n    for i in $(seq 0 $last); do\n\tbundle_node=${BUNDLE_NAME}-${i}\n\tcrm_failcount -q -G -r $NAME -N $bundle_node\n    done\n}\n\ndid_resource_failed_locally() {\n    local failures\n    local running\n    local remotehost\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# if we're dealing with an ocf resource, it is running on a\n\t# pacemaker_remote rather that on the real host, and the\n\t# failcounts are thus associated to the pcmk remote. Replace\n\t# the host's name with the pcmk remote's name.\n        remotehost=$(crm_mon --as-xml | xmllint --xpath \"string(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource/node[@name='${HOST}']/../../resource[@resource_agent='${OCF}:pacemaker:remote']/@id)\" -)\n\tif [ -n \"${remotehost}\" ]; then\n\t    crm_failcount -q -G -r $NAME -N $remotehost | grep -q -w INFINITY\n\t    return $?\n\tfi\n\t# If no pcmk remote is currently running, the failcount from\n\t# the ocf resource is useless, compute the failcount from the\n\t# bundle case instead (computed below).\n    fi\n\n    # for bundles, pacemaker can run any bundle replica locally\n    # (e.g. galera-bundle-docker-{0,1,2}), and a failure happens when\n    # there are no more replica to try.\n    # That is, when _at least_ one replica failed locally, and all the\n    # others either failed or are currently running elsewhere.\n    failures=$(bundle_failures_locally $HOST | grep -c -w INFINITY)\n    running=$(bundle_running_globally)\n    test $failures -gt 0 && \\\n    test $(( $failures + $running )) -ge $BUNDLE_REPLICAS\n}\n\ndid_resource_failed_globally() {\n    local remotecount\n    local failures\n    if [ \"${NAME}\" != \"${BUNDLE_NAME}\" ]; then\n\t# we check the state of an ocf resource only if the\n\t# pcmkremotes are started\n        remotecount=$(crm_mon --as-xml | xmllint --xpath \"count(//resources/bundle[@id='${BUNDLE_NAME}']/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node)\" -)\n\tif [ \"${remotecount}\" = \"0\" ]; then\n\t    # no pcmkremote is running, so check the bundle state\n\t    # instead of checking the ocf resource\n\t    # bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\t    failures=$(bundle_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\telse\n\t    # ocf resource failed if it failed to start on\n\t    # all $BUNDLE_REPLICAS bundle nodes\n\t    failures=$(ocf_failures_globally | grep -c -w INFINITY)\n\t    test $failures -eq $BUNDLE_REPLICAS\n\tfi\n    else\n\t# bundle failed if all ${BUNDLE_REPLICAS} replicas failed\n\tfailures=$(bundle_failures_globally | grep -c -w INFINITY)\n\ttest $failures -eq $BUNDLE_REPLICAS\n    fi\n}\n\n\n# Input validation\n#\n\nNAME=$1\nif [ -z \"${NAME}\" ]; then\n    echo 2>&1 \"Error: argument NAME must not be empty\"\n    exit 1\nfi\n\nBUNDLE_NAME=$2\nif [ -z \"${BUNDLE_NAME}\" ]; then\n    echo 2>&1 \"Error: argument BUNDLE_NAME must not be empty\"\n    exit 1\nfi\n\nROLE_LOCAL=$3\nif [ \"${ROLE_LOCAL}\" = \"_\" ]; then\n    ROLE_LOCAL=\"\"\nfi\n\nROLE_ANYWHERE=$4\nif [ \"${ROLE_ANYWHERE}\" = \"_\" ]; then\n    ROLE_ANYWHERE=\"\"\nfi\n\nif [ -z \"${ROLE_LOCAL}\" ]; then\n    if [ -z \"${ROLE_ANYWHERE}\" ]; then\n        echo 2>&1 \"Error: either ROLE_LOCAL or ROLE_ANYWHERE must be non empty\"\n        exit 1\n    fi\nelse\n    if !(echo \"${ROLE_LOCAL}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n        echo 2>&1 \"Error: argument ROLE_LOCAL must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n        exit 1\n    fi\nfi\n\nif [ -n \"${ROLE_ANYWHERE}\" ] && !(echo \"${ROLE_ANYWHERE}\" | grep -q -x -E \"(Started|Slave|Master|Unpromoted|Promoted)\"); then\n    echo 2>&1 \"Error: argument ROLE_ANYWHERE must be either 'Started' 'Slave' 'Master' 'Unpromoted' or 'Promoted'\"\n    exit 1\nfi\n\n# Ensure compatibility with pacemaker 2.1\nif pacemaker_supports_promoted; then\n    ROLE_LOCAL=$(echo \"$ROLE_LOCAL\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    ROLE_ANYWHERE=$(echo \"$ROLE_ANYWHERE\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    OCF=\"ocf\"\nelse\n    OCF=\"ocf:\"\nfi\n\nHOST=${5:-$(facter hostname)}\nTIMEOUT=${6:-600}\n\n\n# Configure the search\n# ----\n# Note: we can't use crm_resource in all searches because we can't\n# easily extract the host the OCF resources run on (crm_resource\n# returns the pcmk-remote nodes rather than the hosts)\n# So instead, we implement various searches with XPath directly.\n\nif [ \"${BUNDLE_NAME}\" != \"${NAME}\" ]; then\n# ocf resource\nlocal_resource_xpath=\"//bundle/replica/resource[@resource_agent='${OCF}:pacemaker:remote']/node[@name='${HOST}']/../../resource[@id='${NAME}']\"\nany_resource_xpath=\"//bundle//resource[@id='${NAME}']\"\nreplicas_xpath=\"//bundle/primitive[@id='${BUNDLE_NAME}']/../*[boolean(@image) and boolean(@replicas)]\"\nelse\n# bundle resource\nlocal_resource_xpath=\"//bundle[@id='${NAME}']/replica/resource/node[@name='${HOST}']/../../resource\"\nany_resource_xpath=\"//bundle[@id='${NAME}']//resource\"\nreplicas_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nfi\n\nbundle_def_xpath=\"//bundle[@id='${BUNDLE_NAME}']/*[boolean(@image) and boolean(@replicas)]\"\nBUNDLE_CONTAINER_ENGINE=$(cibadmin -Q | xmllint --xpath \"name(${bundle_def_xpath})\" -)\nBUNDLE_REPLICAS=$(cibadmin -Q | xmllint --xpath \"string(${bundle_def_xpath}/@replicas)\" -)\n\n\n# The wait algorithm follows a two-stage approach\n#  1. Depending on how the script is called, we first check whether\n#     the resource is restarted locally. An A/P resource may be\n#     restarted elsewhere in the cluster.\n#  2. If needed, check whether the A/P resource has restarted\n#     elsewhere. For A/P M/S resources, in case the resource is\n#     restarted as Slave locally, ensure a Master is available.\n\nsuccess=1\nbailout=1\ntimeout=$TIMEOUT\nrole=\"\"\n\n# Stage 1: local check\nif [ -n \"$ROLE_LOCAL\" ]; then\n    log \"Waiting until ${NAME} has restarted on ${HOST} and is in state ${ROLE_LOCAL}\"\n    log \"Will probe resource state with the following XPath pattern: ${local_resource_xpath}\"\n\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resource=$(crm_mon -r --as-xml | xmllint --xpath \"${local_resource_xpath}\" - 2>/dev/null)\n        role=$(echo \"${resource}\" | sed -ne 's/.*\\Wrole=\"\\([^\"]*\\)\".*/\\1/p')\n\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resource}\" | grep -q -w \"\\Wblocked=\\\"true\\\"\"; then\n            log \"${NAME} is blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_locally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif [ \"$role\" = \"$ROLE_LOCAL\" ]; then\n            success=0\n            continue\n        elif [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" = \"$ROLE_ANYWHERE\" ]; then\n            # A/P: we are restarted in the expected state\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_LOCAL} on ${HOST}\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\n# Stage 2: global check\nif [ $timeout -gt 0 ] && [ -n \"$ROLE_ANYWHERE\" ] && [ \"$role\" != \"$ROLE_ANYWHERE\" ]; then\n    log \"Wait until ${NAME} is restarted anywhere in the cluster in state ${ROLE_ANYWHERE}\"\n    log \"Will probe resource state with the following XPath pattern: ${any_resource_xpath}\"\n\n    success=1\n    bailout=1\n    while [ $timeout -gt 0 ] && [ $bailout -ne 0 ] && [ $success -ne 0 ]; do\n        resources=$(crm_mon -r --as-xml | xmllint --xpath \"${any_resource_xpath}\" - 2>/dev/null)\n\tif [ \"$(crm_resource --meta -r ${NAME} -g is-managed 2>/dev/null)\" = \"false\" ]; then\n            log \"${NAME} is unmanaged, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif [ \"$(crm_resource --meta -r ${NAME} -g target-role 2>/dev/null)\" = \"Stopped\" ]; then\n            log \"${NAME} is disabled, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif ! (echo \"${resources}\" | grep -q -w \"\\Wblocked=\\\"false\\\"\"); then\n            log \"${NAME} blocked, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n\telif did_resource_failed_globally; then\n            log \"${NAME} is in failed state, will never reach target role. Bailing out\"\n            bailout=0\n            continue\n        elif echo \"${resources}\" | grep -q -w \"\\Wrole=\\\"${ROLE_ANYWHERE}\\\"\"; then\n            success=0\n            continue\n        else\n            log \"Waiting for ${NAME} to transition to role ${ROLE_ANYWHERE} anywhere in the cluster\"\n        fi\n\n        if [ $bailout -ne 0 ] && [ $success -ne 0 ]; then\n            sleep 4\n            timeout=$((timeout-4))\n        fi\n    done\nfi\n\nif [ $timeout -le 0 ]; then\n    log \"Timeout reached after ${TIMEOUT}s while waiting for ${NAME} to be restarted\"\nelif [ $bailout -le 0 ]; then\n    log \"Restart monitoring for ${NAME} cancelled\"\nfi\n\nif [ $success -eq 0 ]; then\n    log \"${NAME} successfully restarted\"\nelse\n    log \"${NAME} was not restarted properly\"\nfi\n\n# Don't block minor update or stack update if the wait was unsuccessful\nexit 0\n"}, "wait-port-and-run.sh": {"mode": "0755", "content": "#!/usr/bin/bash\nset -eu\n\nHOST=$1\nPORT=$2\n\necho \"$(date -u): Checking whether we can bind to ${HOST}:${PORT}\"\nwhile (ss -Htnl src \"${HOST}\" \"sport = :${PORT}\" | grep -wq \"${PORT}\"); do\n    echo \"$(date -u): ${HOST}:${PORT} still in use, waiting...\";\n    sleep 10;\ndone\n\nshift 2\nCOMMAND=\"$*\"\nif [ -z \"${COMMAND}\" ]; then\n    COMMAND=\"true\"\nfi\nexec $COMMAND\n"}, "pacemaker_resource_lock.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\nMAX_RETRIES=10\nCIB_ENOTFOUND=105\n\nusage() {\n   echo \"Set a global property in the cluster with a validity timestamp.\"\n   echo \"Usage:\"\n   echo \"   $0 --acquire <lock_name> <lock_owner> <lock_ttl_in_seconds>\"\n   echo \"   $0 --release <lock_name> <lock_owner>\"\n   echo\n}\n\nlog() {\n    echo \"$(date -u): $1\" 1>&2\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\nlock_create() {\n    local name=$1\n    local data=$2\n    # cibadmin won't overwrite a key if someone else succeeded to create it concurrently\n    cibadmin --sync-call --scope crm_config --create --xml-text \"<cluster_property_set id='${name}'><nvpair id='${name}-pair' name='${name}' value='${data}'/></cluster_property_set>\" &>/dev/null\n    return $?\n}\n\nlock_update() {\n    local name=$1\n    local expected_data=$2\n    local new_data=$3\n    # we only update the lock we expect to see, so we can't update someone else's lock\n    cibadmin --sync-call --scope crm_config --modify --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" --xml-text \"<nvpair id='${name}-pair' name='${name}' value='${new_data}'/>\" &>/dev/null\n    return $?\n}\n\nlock_delete() {\n    local name=$1\n    local expected_data=$2\n    # we only delete the lock we expect to see, so we can't delete someone else's lock\n    cibadmin --sync-call --scope crm_config --delete --xpath \"//cluster_property_set/nvpair[@name='${name}' and @value='${expected_data}']/..\" &>/dev/null\n    return $?\n}\n\nlock_get() {\n    local lockname=$1\n    local res\n    local rc\n    res=$(cibadmin --query --scope crm_config --xpath \"//cluster_property_set/nvpair[@name='$lockname']\" 2>/dev/null)\n    rc=$?\n    if [ $rc -eq 0 ]; then\n        echo \"$res\" | sed -n 's/.*value=\"\\([^\"]*\\)\".*/\\1/p'\n    fi\n    return $rc\n}\n\nlock_owner() {\n    local lock=$1\n    echo \"$lock\" | cut -d':' -f1\n}\n\nlock_has_expired() {\n    local lock=$1\n    local expiry=$(echo \"$lock\" | cut -d':' -f2)\n    local now=$(date +%s)\n    test $now -ge $expiry\n}\n\n\n# Perform a lock action and restart if the CIB has been modified before\n# committing the lock action\ntry_action() {\n    local fun=$1\n    local lock=$2\n    local requester=$3\n    local args=${4:-}\n    local tries=$MAX_RETRIES\n    local rc=1\n    if [ \"$fun\" = \"lock_acquire\" ] || [ \"$fun\" = \"lock_release\" ]; then\n        log \"Try running $fun\"\n    else\n        return 2\n    fi\n    while [ $rc -ne 0 ]; do\n        $fun $lock $requester $args\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Operation $1 succeeded\"\n            return 0\n        elif [ $rc -eq 3 ]; then\n            # rc == 3 -> CIB changed before push\n            if [ $tries -eq 0 ]; then\n                log \"Failed to commit after $MAX_RETRIES retries. Bailing out.\"\n                return 2\n            else\n                log \"Failed to commit. Retrying operation.\"\n                tries=$(($tries - 1))\n            fi\n        elif [ $rc -eq 2 ]; then\n            # rc == 2 -> unrecoverable cib error (e.g. pacemaker down)\n            log \"Unexpected failure. Bailing out\"\n            return $rc\n        else\n            # rc == 1 -> lock error (not owner, lock doesn't exists)\n            return $rc\n        fi\n    done\n}\n\n# The lock mechanism uses cibadmin's atomic creation so cluster-wide\n# state coherency is guaranteed by pacemaker\nlock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc\n    local lock\n    local expiry\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -n \"$lock\" ]; then\n        lock_has_expired $lock\n        rc=$?\n        if [ $rc -eq 0 ]; then\n            log \"Lock has expired, now available for being held\"\n        else\n            # lock is still held. check whether we're the owner\n            owner=$(lock_owner $lock)\n            if [ \"$owner\" = \"$requester\" ];then\n                log \"Requester already owns the lock, acquiring attempt will just reconfigure the TTL\"\n            else\n                log \"Lock is held by someone else ($owner)\"\n                return 1\n            fi\n        fi\n    else\n        log \"Lock is not held yet\"\n    fi\n\n    # prepare the lock info\n    expiry=$(($(date +%s) + $ttl))\n\n    if [ -n \"$lock\" ]; then\n        log \"Attempting to update the lock\"\n        lock_update $lockname \"$lock\" \"$requester:$expiry\"\n        rc=$?\n    else\n        log \"Attempting to acquire the lock\"\n        lock_create $lockname \"$requester:$expiry\"\n        rc=$?\n    fi\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' acquired by '$requester', valid until $(date -d @$expiry)\"\n        return 0\n    else\n        log \"CIB changed, lock cannot be acquired\"\n        return 3\n    fi\n}\n\n\n# The lock mechanism uses the CIB's num_updates tag to implement\n# a conditional store. Cluster-wide locking is guaranteed by pacemaker\nlock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n    local lock\n    local owner\n\n    log \"Check whether the lock is already held in the CIB\"\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        log \"Could not retrieve info from the CIB\"\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        log \"Lock doesn't exist. Nothing to release\"\n        return 0\n    else\n        log \"Lock exists, check whether we're the owner\"\n        owner=$(lock_owner $lock)\n        if [ \"$owner\" != \"$requester\" ];then\n            log \"Lock is held by someone else ($owner), will not unlock\"\n            return 1\n        fi\n    fi\n\n    lock_delete $lockname \"$lock\"\n    rc=$?\n\n    if [ $rc -eq 0 ]; then\n        log \"Lock '$lockname' released by '$requester'\"\n        return 0\n    else\n        log \"CIB deletion error, lock cannot be released\"\n        return 3\n    fi\n}\n\n\n# Retrieve the owner of a lock from the CIB\n# this is a read-only operation, so no need to log debug info\nlock_get_owner() {\n    local lockname=$1\n    local rc\n    local lock\n    local owner\n\n    lock=$(lock_get $lockname)\n    rc=$?\n    if [ $rc -ne 0 ] && [ $rc -ne $CIB_ENOTFOUND ]; then\n        return 2\n    fi\n\n    if [ -z \"$lock\" ]; then\n        return 1\n    else\n        lock_owner $lock\n        return 0\n    fi\n}\n\n\nACTION=$1\nLOCKNAME=$2\nREQUESTER=$3\nTTL=${4:-60}\n\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nif [ $ACTION != \"--help\" ]; then\n    if [ -z \"$LOCKNAME\" ]; then\n        error \"You must specific a lock name\"\n    fi\n    if [ $ACTION != \"--owner\" ] && [ $ACTION != \"-o\" ]; then\n        if [ -z \"$REQUESTER\" ]; then\n            error \"You must specific a lock requester\"\n        fi\n    fi\nfi\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) try_action lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --release|-r) try_action lock_release $LOCKNAME $REQUESTER;;\n    --acquire-once|-A) lock_acquire $LOCKNAME $REQUESTER $TTL;;\n    --owner|-o) lock_get_owner $LOCKNAME;;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "pacemaker_mutex_restart_bundle.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_restart_bundle.sh --lock mysql galera galera-bundle Master _\n# pacemaker_mutex_restart_bundle.sh --lock ovn_dbs ovndb_servers ovn-dbs-bundle Slave Master\n\nset -u\n\nusage() {\n    echo \"Restart a clustered resource in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --lock <tripleo-service> <pcmk-resource> <pcmk-bundle> <target-state-local> <target-state-cluster>\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\npacemaker_supports_promoted() {\n    # The Promoted token is only matched in recent pacemaker versions\n    grep -wq \"<value>Promoted</value>\" /usr/share/pacemaker/resources-*.rng\n}\n\nACTION=$1\ncase $ACTION in\n    --help) usage; exit 0;;\n    --lock) ;;\n    *) error \"Unknown action '$ACTION'\";;\nesac\n\nTRIPLEO_SERVICE=$2\nLOCK_NAME=${TRIPLEO_SERVICE}-restart-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to restart resource $TRIPLEO_SERVICE\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\nRESOURCE_NAME=$3\nBUNDLE_NAME=$4\nWAIT_TARGET_LOCAL=$5\nWAIT_TARGET_ANYWHERE=${6:-_}\n\nif pacemaker_supports_promoted; then\n    WAIT_TARGET_LOCAL=$(echo \"$5\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    WAIT_TARGET_ANYWHERE=$(echo \"${6:-_}\" | sed -e 's/Master/Promoted/' -e 's/Slave/Unpromoted/')\n    promoted_role=\"Promoted\"\nelse\n    promoted_role=\"Master\"\nfi\n\n# The lock TTL should accomodate for the resource start/promote timeout\nif [ \"$RESOURCE_NAME\" != \"$BUNDLE_NAME\" ]; then\n    if [ \"$WAIT_TARGET_LOCAL\" = \"$promoted_role\" ] || [ \"$WAIT_TARGET_ANYWHERE\" = \"$promoted_role\" ]; then\n        rsc_op=\"promote\"\n    else\n        rsc_op=\"start\"\n    fi\n    # <op id=\"galera-promote-interval-0s\" interval=\"0s\" name=\"promote\" on-fail=\"block\" timeout=\"300s\"/>\n    PCMK_TTL=$(cibadmin -Q | xmllint -xpath \"string(//primitive[@id='${RESOURCE_NAME}']/operations/op[@name='${rsc_op}']/@timeout)\" - | sed 's/s$//')\n    LOCK_TTL=$((PCMK_TTL + 30))\nelse\n    # The podman RA's default start timeout\n    LOCK_TTL=90\nfi\n\nlog \"Acquire a ${LOCK_TTL}s restart lock for service $TRIPLEO_SERVICE before restarting it\"\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nrc=1\nwhile [ $rc -ne 0 ]; do\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire $LOCK_NAME $LOCK_OWNER $LOCK_TTL\n    rc=$?\n    if [ $rc != 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not acquire lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Could not acquire lock, retrying\"\n            sleep 10\n        fi\n    fi\ndone\n\nlog \"Restart the service $TRIPLEO_SERVICE locally\"\n# Reuse the local restart script in t-h-t (driven by env var TRIPLEO_MINOR_UPDATE)\nTRIPLEO_MINOR_UPDATE=true /var/lib/container-config-scripts/pacemaker_restart_bundle.sh $TRIPLEO_SERVICE $RESOURCE_NAME $BUNDLE_NAME $WAIT_TARGET_LOCAL $WAIT_TARGET_ANYWHERE\n\n# If we reached this point, always try to release the lock\nlog \"Release the restart lock for service $TRIPLEO_SERVICE\"\n/var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $LOCK_NAME $LOCK_OWNER\nrc=$?\nif [ $rc -ne 0 ] && [ $rc -ne 1 ]; then\n    error \"Could not release held lock (rc: $rc)\"\nfi\n"}, "pacemaker_mutex_shutdown.sh": {"mode": "0755", "content": "#!/usr/bin/bash\n\n# pacemaker_mutex_shutdown.sh --acquire\n# pacemaker_mutex_shutdown.sh --release\n\nset -u\n\nusage() {\n    echo \"Shutdown a cluster node in a coordinated way across the cluster\"\n    echo \"Usage:\"\n    echo \"   $0 --acquire # prevent other node from shutting down until we hold the lock\"\n    echo \"   $0 --release # release the lock, other node can compete for the shutdown lock\"\n    echo\n}\n\nlog() {\n    echo \"$(date -u): $1\"\n}\n\nerror() {\n    echo \"$(date -u): $1\" 1>&2\n    exit 1\n}\n\n# Loop until we hold the lock. The lock has a TTL, so we're guaranteed to get it eventually\nshutdown_lock_acquire() {\n    local lockname=$1\n    local requester=$2\n    local ttl=$3\n    local rc=1\n    local current_owner\n    local owner_stopped\n    local owner_rc\n\n    log \"Acquiring the shutdown lock\"\n    while [ $rc -ne 0 ]; do\n        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --acquire-once $lockname $requester $ttl\n        rc=$?\n        if [ $rc -ne 0 ]; then\n            if [ $rc -eq 2 ]; then\n                error \"Could not acquire the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n            else\n                # The lock is held by another node.\n                current_owner=$(/var/lib/container-config-scripts/pacemaker_resource_lock.sh --owner $lockname)\n                owner_rc=$?\n                if [ $owner_rc -eq 2 ]; then\n                    error \"Could not get the shutdown lock owner due to unrecoverable error (rc: $owner_rc), bailing out\"\n                fi\n                if [ $owner_rc -eq 0 ]; then\n                    # If the owner is marked as offline, that means it has shutdown and\n                    # we can clean the lock preemptively and try to acquire it.\n                    owner_stopped=$(crm_mon -1X | xmllint --xpath 'count(//nodes/node[@name=\"'${current_owner}'\" and @online=\"false\" and @unclean=\"false\"])' -)\n                    if [ \"${owner_stopped}\" = \"1\" ]; then\n                        log \"Shutdown lock held by stopped node '${current_owner}', lock can be released\"\n                        /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $current_owner\n                        continue\n                    fi\n                fi\n                log \"Shutdown lock held by another node (rc: $rc), retrying\"\n                sleep 10\n            fi\n        fi\n    done\n    log \"Shutdown lock acquired\"\n    return 0\n}\n\n\n# Release the lock if we still own it. Not owning it anymore is not fatal\nshutdown_lock_release() {\n    local lockname=$1\n    local requester=$2\n    local rc\n\n    log \"Releasing the shutdown lock\"\n    /var/lib/container-config-scripts/pacemaker_resource_lock.sh --release $lockname $requester\n    rc=$?\n    if [ $rc -ne 0 ]; then\n        if [ $rc -gt 1 ]; then\n            error \"Could not release the shutdown lock due to unrecoverable error (rc: $rc), bailing out\"\n        else\n            log \"Shutdown lock no longer held, nothing to do\"\n        fi\n    else\n        log \"Shutdown lock released\"\n    fi\n    return 0\n}\n\n\nACTION=$1\nif [ -z \"$ACTION\" ]; then\n    error \"Action must be specified\"\nfi\n\nLOCK_NAME=tripleo-shutdown-lock\nLOCK_OWNER=$(crm_node -n 2>/dev/null)\nrc=$?\nif [ $rc -ne 0 ]; then\n    if [ $rc -eq 102 ]; then\n        log \"Cluster is not running locally, no need to aquire the shutdown lock\"\n        exit 0\n    else\n        error \"Unexpected error while connecting to the cluster (rc: $rc), bailing out\"\n    fi\nfi\n\n# We start with a very high TTL, that long enough to accomodate a cluster stop.\n# As soon as the node will get offline, the other competing node will be entitled\n# to steal the lock, so they should never wait that long in practice.\nLOCK_TTL=600\n\n\ncase $ACTION in\n    --help) usage; exit 0;;\n    --acquire|-a) shutdown_lock_acquire ${LOCK_NAME} ${LOCK_OWNER} ${LOCK_TTL};;\n    --release|-r) shutdown_lock_release ${LOCK_NAME} ${LOCK_OWNER};;\n    *) error \"Invalid action\";;\nesac\nexit $?\n"}, "nova_statedir_ownership.py": {"mode": "0700", "content": "#!/usr/bin/python3\n#\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\nimport logging\nimport os\nimport pwd\nimport selinux\nimport stat\nimport sys\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_statedir')\n\n\nclass PathManager(object):\n    \"\"\"Helper class to manipulate ownership of a given path\"\"\"\n    def __init__(self, path):\n        self.path = path\n        self.uid = None\n        self.gid = None\n        self.is_dir = None\n        self.secontext = None\n        self._update()\n\n    def _update(self):\n        try:\n            statinfo = os.stat(self.path)\n            self.is_dir = stat.S_ISDIR(statinfo.st_mode)\n            self.uid = statinfo.st_uid\n            self.gid = statinfo.st_gid\n            self.secontext = selinux.lgetfilecon(self.path)[1]\n        except Exception:\n            LOG.exception('Could not update metadata for %s', self.path)\n            raise\n\n    def __str__(self):\n        return \"uid: {} gid: {} path: {}{}\".format(\n            self.uid,\n            self.gid,\n            self.path,\n            '/' if self.is_dir else ''\n        )\n\n    def has_owner(self, uid, gid):\n        return self.uid == uid and self.gid == gid\n\n    def has_either(self, uid, gid):\n        return self.uid == uid or self.gid == gid\n\n    def chown(self, uid, gid):\n        target_uid = -1\n        target_gid = -1\n        if self.uid != uid:\n            target_uid = uid\n        if self.gid != gid:\n            target_gid = gid\n        if (target_uid, target_gid) != (-1, -1):\n            LOG.info('Changing ownership of %s from %d:%d to %d:%d',\n                     self.path,\n                     self.uid,\n                     self.gid,\n                     self.uid if target_uid == -1 else target_uid,\n                     self.gid if target_gid == -1 else target_gid)\n            try:\n                os.chown(self.path, target_uid, target_gid)\n                self._update()\n            except Exception:\n                LOG.exception('Could not change ownership of %s: ',\n                              self.path)\n                raise\n        else:\n            LOG.info('Ownership of %s already %d:%d',\n                     self.path,\n                     uid,\n                     gid)\n\n    def chcon(self, context):\n        # If dir returns whether to recusively set context\n        try:\n            try:\n                selinux.lsetfilecon(self.path, context)\n                LOG.info('Setting selinux context of %s to %s',\n                     self.path, context)\n                return True\n            except OSError as e:\n                if self.is_dir and e.errno == 95:\n                    # Operation not supported, assume NFS mount and skip\n                    LOG.info('Setting selinux context not supported for %s',\n                             self.path)\n                    return False\n                else:\n                    raise\n        except Exception:\n            LOG.exception('Could not set selinux context of %s to %s:',\n                          self.path, context)\n            raise\n\n\nclass NovaStatedirOwnershipManager(object):\n    \"\"\"Class to manipulate the ownership of the nova statedir (/var/lib/nova).\n\n       The nova uid/gid differ on the host and container images. An upgrade\n       that switches from host systemd services to docker requires a change in\n       ownership. Previously this was a naive recursive chown, however this\n       causes issues if nova instance are shared via an NFS mount: any open\n       filehandles in qemu/libvirt fail with an I/O error (LP1778465).\n\n       Instead the upgrade/FFU ansible tasks now lay down a marker file when\n       stopping and disabling the host systemd services. We use this file to\n       determine the host nova uid/gid. We then walk the tree and update any\n       files that have the host uid/gid to the docker nova uid/gid. As files\n       owned by root/qemu etc... are ignored this avoids the issues with open\n       filehandles. The marker is removed once the tree has been walked.\n\n       For subsequent runs, or for a new deployment, we simply ensure that the\n       docker nova user/group owns all directories. This is required as the\n       directories are created with root ownership in host_prep_tasks (the\n       docker nova uid/gid is not known in this context).\n    \"\"\"\n    def __init__(self, statedir, upgrade_marker='upgrade_marker',\n                 nova_user='nova', secontext_marker='../_nova_secontext',\n                 exclude_paths=None):\n        self.statedir = statedir\n        self.nova_user = nova_user\n\n        self.upgrade_marker_path = os.path.join(statedir, upgrade_marker)\n        self.secontext_marker_path = os.path.normpath(os.path.join(statedir, secontext_marker))\n        self.upgrade = os.path.exists(self.upgrade_marker_path)\n\n        self.exclude_paths = [self.upgrade_marker_path]\n        if exclude_paths is not None:\n            for p in exclude_paths:\n                if not p.startswith(os.path.sep):\n                    p = os.path.join(self.statedir, p)\n                self.exclude_paths.append(p)\n\n        self.target_uid, self.target_gid = self._get_nova_ids()\n        self.previous_uid, self.previous_gid = self._get_previous_nova_ids()\n        self.id_change = (self.target_uid, self.target_gid) != \\\n            (self.previous_uid, self.previous_gid)\n        self.target_secontext = self._get_secontext()\n\n    def _get_nova_ids(self):\n        nova_uid, nova_gid = pwd.getpwnam(self.nova_user)[2:4]\n        return nova_uid, nova_gid\n\n    def _get_previous_nova_ids(self):\n        if self.upgrade:\n            statinfo = os.stat(self.upgrade_marker_path)\n            return statinfo.st_uid, statinfo.st_gid\n        else:\n            return self._get_nova_ids()\n\n    def _get_secontext(self):\n        if os.path.exists(self.secontext_marker_path):\n            return selinux.lgetfilecon(self.secontext_marker_path)[1]\n        else:\n            return None\n\n    def _walk(self, top, chcon=True):\n        for f in os.listdir(top):\n            pathname = os.path.join(top, f)\n\n            if pathname in self.exclude_paths:\n                continue\n\n            try:\n                pathinfo = PathManager(pathname)\n                LOG.info(\"Checking %s\", pathinfo)\n                if pathinfo.is_dir:\n                    # Always chown the directories\n                    pathinfo.chown(self.target_uid, self.target_gid)\n                    chcon_r = chcon\n                    if chcon:\n                        chcon_r = pathinfo.chcon(self.target_secontext)\n                    self._walk(pathname, chcon_r)\n                elif self.id_change:\n                    # Only chown files if it's an upgrade and the file is owned by\n                    # the host nova uid/gid\n                    pathinfo.chown(\n                        self.target_uid if pathinfo.uid == self.previous_uid\n                        else pathinfo.uid,\n                        self.target_gid if pathinfo.gid == self.previous_gid\n                        else pathinfo.gid\n                    )\n                    if chcon:\n                        pathinfo.chcon(self.target_secontext)\n            except Exception:\n                # Likely to have been caused by external systems\n                # interacting with this directory tree,\n                # especially on NFS e.g snapshot dirs.\n                # Just ignore it and continue on to the next entry\n                continue\n\n    def run(self):\n        LOG.info('Applying nova statedir ownership')\n        LOG.info('Target ownership for %s: %d:%d',\n                 self.statedir,\n                 self.target_uid,\n                 self.target_gid)\n\n        pathinfo = PathManager(self.statedir)\n        LOG.info(\"Checking %s\", pathinfo)\n        pathinfo.chown(self.target_uid, self.target_gid)\n        chcon = self.target_secontext is not None\n\n        if chcon:\n            pathinfo.chcon(self.target_secontext)\n\n        self._walk(self.statedir, chcon)\n\n        if self.upgrade:\n            LOG.info('Removing upgrade_marker %s',\n                     self.upgrade_marker_path)\n            os.unlink(self.upgrade_marker_path)\n\n        LOG.info('Nova statedir ownership complete')\n\n\ndef get_exclude_paths():\n    exclude_paths = os.environ.get('NOVA_STATEDIR_OWNERSHIP_SKIP')\n    if exclude_paths is not None:\n        exclude_paths = exclude_paths.split(os.pathsep)\n    return exclude_paths\n\n\nif __name__ == '__main__':\n    NovaStatedirOwnershipManager('/var/lib/nova', exclude_paths=get_exclude_paths()).run()\n"}, "nova_wait_for_compute_service.py": {"mode": "0755", "content": "#!/usr/bin/python3\n#\n# Copyright 2018 Red Hat Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\nimport argparse\nimport logging\nimport os\nimport six\nimport socket\nimport sys\nimport time\n\n\nfrom keystoneauth1 import loading\nfrom keystoneauth1 import session\n\nfrom novaclient import client\n\n# In python3 SafeConfigParser was renamed to ConfigParser and the default\n# for duplicate options default to true. In case of nova it is valid to\n# have duplicate option lines, e.g. passthrough_whitelist which leads to\n# issues reading the nova.conf\n# https://bugs.launchpad.net/tripleo/+bug/1827775\nif six.PY3:\n    from six.moves.configparser import ConfigParser\n    config = ConfigParser(strict=False)\nelse:\n    from six.moves.configparser import SafeConfigParser\n    config = SafeConfigParser()\n\ndebug = os.getenv('__OS_DEBUG', 'false')\n\nif debug.lower() == 'true':\n    loglevel = logging.DEBUG\nelse:\n    loglevel = logging.INFO\n\nlogging.basicConfig(stream=sys.stdout, level=loglevel)\nLOG = logging.getLogger('nova_wait_for_compute_service')\n\niterations = 60\ntimeout = 10\nnova_cfg = '/etc/nova/nova.conf'\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(usage='%(prog)s [options]')\n    parser.add_argument('-k', '--insecure',\n                        action=\"store_false\",\n                        dest='insecure',\n                        default=True,\n                        help='Allow insecure connection when using SSL')\n\n    args = parser.parse_args()\n    LOG.debug('Running with parameter insecure = %s',\n              args.insecure)\n\n    if os.path.isfile(nova_cfg):\n        try:\n            config.read(nova_cfg)\n        except Exception:\n            LOG.exception('Error while reading nova.conf:')\n    else:\n        LOG.error('Nova configuration file %s does not exist', nova_cfg)\n        sys.exit(1)\n\n    my_host = config.get('DEFAULT', 'host')\n    if not my_host:\n        # If host isn't set nova defaults to this\n        my_host = socket.gethostname()\n\n    loader = loading.get_plugin_loader('password')\n    auth = loader.load_from_options(\n        auth_url=config.get('neutron',\n                            'auth_url'),\n        username=config.get('neutron',\n                            'username'),\n        password=config.get('neutron',\n                            'password'),\n        project_name=config.get('neutron',\n                                'project_name'),\n        project_domain_name=config.get('neutron',\n                                       'project_domain_name'),\n        user_domain_name=config.get('neutron',\n                                    'user_domain_name'))\n    sess = session.Session(auth=auth, verify=args.insecure)\n    nova = client.Client('2.11', session=sess, endpoint_type='internal',\n                         region_name=config.get('neutron', 'region_name'))\n\n    # Wait until this host is listed in the service list\n    for i in range(iterations):\n        try:\n            service_list = nova.services.list(binary='nova-compute')\n            for entry in service_list:\n                host = getattr(entry, 'host', '')\n                zone = getattr(entry, 'zone', '')\n                if host == my_host and zone != 'internal':\n                    LOG.info('Nova-compute service registered')\n                    sys.exit(0)\n            LOG.info('Waiting for nova-compute service to register')\n        except Exception:\n            LOG.exception(\n                'Error while waiting for nova-compute service to register')\n        time.sleep(timeout)\nsys.exit(1)\n\n# vim: set et ts=4 sw=4 :\n"}}, "docker_config": {"step_2": {"nova_compute_init_log": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "net": "none", "privileged": false, "user": "root", "volumes": ["/var/log/containers/nova:/var/log/nova:z"], "command": ["/bin/bash", "-c", "chown -R nova:nova /var/log/nova"], "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725"}}}, "step_3": {"nova_statedir_owner": {"image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "net": "none", "user": "root", "security_opt": ["label=disable"], "privileged": false, "detach": false, "volumes": ["/var/lib/nova:/var/lib/nova:shared", "/var/lib/_nova_secontext:/var/lib/_nova_secontext:shared,z", "/var/lib/container-config-scripts:/container-config-scripts:z"], "command": "/container-config-scripts/pyshim.sh /container-config-scripts/nova_statedir_ownership.py", "environment": {"TRIPLEO_DEPLOY_IDENTIFIER": "1781335725", "__OS_DEBUG": "true", "NOVA_STATEDIR_OWNERSHIP_SKIP": "triliovault-mounts"}}}, "step_5": {"nova_compute": {"start_order": 3, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "ulimit": ["nofile=131072", "memlock=67108864"], "ipc": "host", "net": "host", "privileged": true, "user": "nova", "restart": "always", "depends_on": ["tripleo_nova_libvirt.target"], "healthcheck": {"test": "/openstack/healthcheck 5672"}, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/log/containers/nova:/var/log/nova", "/etc/ssh/ssh_known_hosts:/etc/ssh/ssh_known_hosts:ro", "/var/lib/kolla/config_files/nova_compute.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova_libvirt:/var/lib/kolla/config_files/src:ro", "/var/lib/config-data/puppet-generated/iscsid/etc/iscsi:/var/lib/kolla/config_files/src-iscsid:ro", "/var/lib/tripleo-config/ceph:/var/lib/kolla/config_files/src-ceph:z", "/dev:/dev", "/lib/modules:/lib/modules:ro", "/run:/run", "/run/nova:/run/nova:z", "/var/lib/iscsi:/var/lib/iscsi:z", "/var/lib/libvirt:/var/lib/libvirt:shared", "/sys/class/net:/sys/class/net", "/sys/bus/pci:/sys/bus/pci", "/boot:/boot:ro", "/var/lib/nova:/var/lib/nova:shared"], "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS", "LIBGUESTFS_BACKEND": "direct"}}, "nova_wait_for_compute_service": {"start_order": 4, "image": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "net": "host", "detach": false, "volumes": ["/etc/hosts:/etc/hosts:ro", "/etc/localtime:/etc/localtime:ro", "/etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted:ro", "/etc/pki/ca-trust/source/anchors:/etc/pki/ca-trust/source/anchors:ro", "/etc/pki/tls/certs/ca-bundle.crt:/etc/pki/tls/certs/ca-bundle.crt:ro", "/etc/pki/tls/certs/ca-bundle.trust.crt:/etc/pki/tls/certs/ca-bundle.trust.crt:ro", "/etc/pki/tls/cert.pem:/etc/pki/tls/cert.pem:ro", "/dev/log:/dev/log", "/etc/ipa/ca.crt:/etc/ipa/ca.crt:ro", "/etc/puppet:/etc/puppet:ro", "/var/lib/kolla/config_files/nova_compute_wait_for_compute_service.json:/var/lib/kolla/config_files/config.json:ro", "/var/lib/config-data/puppet-generated/nova_libvirt:/var/lib/kolla/config_files/src:ro", "/var/log/containers/nova:/var/log/nova", "/var/lib/container-config-scripts:/container-config-scripts"], "user": "nova", "environment": {"KOLLA_CONFIG_STRATEGY": "COPY_ALWAYS", "__OS_DEBUG": "true"}}}}, "host_prep_tasks": [{"name": "create persistent directories", "file": {"path": "{{ item.path }}", "setype": "{{ item.setype }}", "state": "directory", "mode": "{{ item.mode }}"}, "with_items": [{"path": "/var/log/containers/nova", "setype": "container_file_t", "mode": "0750"}]}, {"name": "Check for /var/lib/nova/instances directory exitance", "stat": {"path": "/var/lib/nova/instances"}, "register": "check_nova_instances_result"}, {"name": "create persistent directories", "file": {"path": "{{ item.path }}", "state": "directory", "setype": "{{ item.setype | default(omit) }}"}, "with_items": [{"path": "/var/lib/nova", "setype": "container_file_t"}, {"path": "/var/lib/_nova_secontext", "setype": "container_file_t"}, {"path": "/var/lib/nova/instances", "setype": "container_file_t"}, {"path": "/var/lib/libvirt", "setype": "container_file_t"}, {"path": "/etc/tmpfiles.d"}]}, {"name": "ensure /run/nova is present upon reboot", "copy": {"dest": "/etc/tmpfiles.d/run-nova.conf", "content": "d /run/nova 0777 root root - -\n"}}, {"name": "create tempfiles", "command": "systemd-tmpfiles --create"}, {"name": "Mount Nova NFS Share", "vars": {"nfs_backend_enable": false, "nfs_share": "", "nfs_options": "context=system_u:object_r:nfs_t:s0", "nfs_vers": "4.2"}, "mount": {"name": "/var/lib/nova/instances", "state": "mounted", "fstype": "nfs4", "src": "{{nfs_share}}", "opts": "_netdev,bg,{{nfs_options}},nfsvers={{nfs_vers}}"}, "when": "nfs_backend_enable|bool"}, {"name": "delay installation of systemd-container", "when": "check_nova_instances_result.stat.exists", "block": [{"name": "check systemd-container package installed or not", "package": {"name": "systemd-container", "state": "present"}, "check_mode": true, "register": "is_systemd_container_package", "failed_when": false}, {"name": "get latest package from downloaded package versions", "shell": "ls -1t /var/tmp/systemd-container* | head -1", "register": "latest_systemd_container_package", "when": ["is_systemd_container_package is defined", "('changed' in is_systemd_container_package)", "is_systemd_container_package.changed"], "failed_when": false}, {"name": "Install systemd-container package on boot", "copy": {"dest": "/etc/systemd/system/install-systemd-container.service", "content": "[Unit]\nDescription=Install systemd container package on system boot\nBefore=tripleo_nova_virtqemud.service\n\n[Service]\nType=oneshot\nExecStart=/usr/bin/rpm -ivh {{ latest_systemd_container_package.stdout }}\nExecStartPost=rm -f /var/tmp/systemd-container*\n\n[Install]\nWantedBy=multi-user.target\n"}, "when": ["latest_systemd_container_package is defined", "('stdout' in latest_systemd_container_package)", "is_systemd_container_package is defined", "('changed' in is_systemd_container_package)", "is_systemd_container_package.changed"]}, {"name": "enable install-systemd-container on compute boot", "systemd": {"name": "install-systemd-container", "state": "stopped", "enabled": true, "daemon_reload": true}, "when": ["is_systemd_container_package is defined", "('changed' in is_systemd_container_package)", "is_systemd_container_package.changed"]}, {"name": "check if install-systemd-container service exists", "stat": {"path": "/etc/systemd/system/install-systemd-container.service"}, "register": "install_systemd_container_result"}, {"name": "disable and mask install-systemd-container on compute boot", "systemd": {"name": "install-systemd-container", "enabled": false, "masked": true, "daemon_reload": true}, "when": ["is_systemd_container_package is defined", "('changed' in is_systemd_container_package)", "not is_systemd_container_package.changed", "install_systemd_container_result.stat.exists"]}]}, {"name": "is Nova Resume Guests State On Host Boot enabled", "set_fact": {"resume_guests_state_on_host_boot_enabled": false}}, {"name": "install libvirt-guests systemd unit file (docker)", "when": ["resume_guests_state_on_host_boot_enabled|bool", "container_cli == 'docker'"], "block": [{"name": "libvirt-guests unit to stop nova_compute container before shutdown VMs", "copy": {"dest": "/etc/systemd/system/libvirt-guests.service", "content": "[Unit]\nDescription=Suspend/Resume Running libvirt Guests\nAfter=network.target\nAfter=time-sync.target\nAfter=virt-guest-shutdown.target\nAfter=docker.service\nAfter=tripleo-container-shutdown.service\nAfter=rhel-push-plugin.service\nDocumentation=man:libvirtd(8)\nDocumentation=https://libvirt.org\n\n[Service]\nEnvironmentFile=-/var/lib/config-data/puppet-generated/nova_libvirt/etc/sysconfig/libvirt-guests\n# Hack just call traditional service until we factor\n# out the code\nExecStart=/bin/{{container_cli}} exec nova_libvirt /bin/sh -x /usr/libexec/libvirt-guests.sh start\nExecStop=/bin/{{container_cli}} stop nova_compute\nExecStop=/bin/{{container_cli}}  exec nova_libvirt /bin/sh -x /usr/libexec/libvirt-guests.sh stop\nType=oneshot\nRemainAfterExit=yes\nStandardOutput=journal+console\nTimeoutStopSec=0\n\n[Install]\nWantedBy=multi-user.target\n"}}, {"name": "Making sure virt-guest-shutdown.target is present", "copy": {"dest": "/etc/systemd/system/virt-guest-shutdown.target", "content": "[Unit]\nDescription=Libvirt guests shutdown\nDocumentation=https://libvirt.org\nAfter=systemd-machined.service\nAfter=network-online.target\nAfter=tripleo_nova_libvirt.target\nBefore=tripleo_nova_compute.service\n"}}, {"name": "libvirt-guests enable VM shutdown on compute reboot/shutdown", "systemd": {"name": "libvirt-guests", "enabled": true, "daemon_reload": true}}]}, {"name": "install tripleo_nova_libvirt_guests systemd unit file (podman)", "when": ["resume_guests_state_on_host_boot_enabled|bool", "container_cli == 'podman'"], "block": [{"name": "libvirt-guests unit to stop nova_compute container before shutdown VMs (monolithic libvirt)", "copy": {"dest": "/etc/systemd/system/tripleo_nova_libvirt_guests.service", "content": "[Unit]\nDescription=Suspend libvirt Guests in tripleo\nRequires=virt-guest-shutdown.target\nAfter=systemd-machined.service\nAfter=network-online.target\nAfter=tripleo_nova_libvirt.target\nBefore=tripleo_nova_compute.service\nDocumentation=man:libvirtd(8)\nDocumentation=https://libvirt.org\n\n[Service]\nEnvironmentFile=-/etc/sysconfig/libvirt-guests\nExecStart=/bin/podman exec nova_libvirt /bin/rm -f /var/lib/libvirt/libvirt-guests\nExecStop=/bin/podman exec nova_libvirt /bin/sh -x /usr/libexec/libvirt-guests.sh shutdown\nType=oneshot\nRemainAfterExit=yes\nStandardOutput=journal+console\nTimeoutStopSec=0\n\n[Install]\nWantedBy=multi-user.target\n"}, "when": "tripleo_nova_libvirt_virsh_container | default('nova_libvirt') == 'nova_libvirt'"}, {"name": "libvirt-guests unit to stop nova_compute container before shutdown VMs (modular libvirt)", "copy": {"dest": "/etc/systemd/system/tripleo_nova_libvirt_guests.service", "content": "[Unit]\nDescription=Suspend libvirt Guests in tripleo\nRequires=virt-guest-shutdown.target\nAfter=systemd-machined.service\nAfter=network-online.target\nAfter=tripleo_nova_libvirt.target\nBefore=tripleo_nova_compute.service\nDocumentation=man:libvirtd(8)\nDocumentation=https://libvirt.org\n\n[Service]\nEnvironmentFile=-/etc/sysconfig/libvirt-guests\nExecStart=/bin/podman exec nova_virtproxyd /bin/rm -f /var/lib/libvirt/libvirt-guests\nExecStop=/bin/podman exec nova_virtproxyd /bin/sh -x /usr/libexec/libvirt-guests.sh shutdown\nType=oneshot\nRemainAfterExit=yes\nStandardOutput=journal+console\nTimeoutStopSec=0\n\n[Install]\nWantedBy=multi-user.target\n"}, "when": "tripleo_nova_libvirt_virsh_container | default('nova_libvirt') != 'nova_libvirt'"}, {"name": "Making sure virt-guest-shutdown.target is present", "copy": {"dest": "/etc/systemd/system/virt-guest-shutdown.target", "content": "[Unit]\nDescription=Libvirt guests shutdown\nDocumentation=https://libvirt.org\nAfter=systemd-machined.service\nAfter=network-online.target\nAfter=tripleo_nova_libvirt.target\nBefore=tripleo_nova_compute.service\n"}}, {"name": "tripleo_nova_libvirt_guests enable VM shutdown on compute reboot/shutdown", "systemd": {"name": "tripleo_nova_libvirt_guests", "enabled": true, "daemon_reload": true}}]}, {"name": "ensure ceph configurations exist", "file": {"path": "/var/lib/tripleo-config/ceph", "state": "directory"}}, {"name": "enable virt_sandbox_use_netlink for healthcheck", "seboolean": {"name": "virt_sandbox_use_netlink", "persistent": true, "state": true}, "when": ["ansible_facts.selinux is defined", "ansible_facts.selinux.status == \"enabled\""]}, {"name": "Do we prepend nova startup with a delay", "set_fact": {"nova_compute_delay": 0}}, {"name": "install nova-compute delay wrapper script", "copy": {"content": "#!/usr/libexec/platform-python\n\"\"\"\nThis wrapper was created to add an optional delay to the startup of nova-compute.\nWe know that instances will fail to boot, after a compute reboot, if ceph is not\nhealthy.\n\nIdeally, we would poll ceph to get its health, but it's not guaranteed that the\ncompute node will have access to the keys.\n\"\"\"\n\nimport os\nimport sys\nimport time\nimport logging\nimport argparse\n\nparser = argparse.ArgumentParser(description='Process some integers.')\nparser.add_argument('--config-file', dest='nova_config', action='store',\n                    default=\"/etc/nova/nova.conf\",\n                    help='path to nova configuration (default: /etc/nova/nova.conf)')\nparser.add_argument('--nova-binary', dest='nova_binary', action='store',\n                    default=\"/usr/bin/nova-compute\",\n                    help='path to nova compute binary (default: /usr/bin/nova-compute)')\nparser.add_argument('--delay', dest='delay', action='store',\n                    default=120, type=int,\n                    help='Number of seconds to wait until nova-compute is started')\nparser.add_argument('--state-dir', dest='state_dir', action='store',\n                    default=\"/run/nova/startup\", help='state-file directory')\nparser.add_argument('--state-file', dest='state_file', action='store',\n                    default=\"nova-compute-delayed\",\n                    help='file exists if we already delayed nova-compute startup')\n\n\nsections = {}\n(args, remaining) = parser.parse_known_args(sys.argv)\n\nreal_args = [args.nova_binary, '--config-file', args.nova_config]\nreal_args.extend(remaining[1:])\n\nif not os.path.isdir(args.state_dir):\n  os.makedirs(args.state_dir, mode = 0o751)\n\nstate_file_name = os.path.join(args.state_dir, args.state_file)\n\nif not os.path.isfile(state_file_name):\n    logging.info(\"Delaying nova-compute startup by %s seconds\" % args.delay)\n    time.sleep(args.delay)\n    open(state_file_name, 'a').close()\n\nlogging.info(\"Executing %s\" % real_args)\nos.execv(args.nova_binary, real_args)\n", "dest": "/var/lib/nova/delay-nova-compute", "mode": 493}, "when": "nova_compute_delay|int > 0"}, {"name": "Is irqbalance enabled", "set_fact": {"compute_irqbalance_disabled": false}}, {"name": "disable irqbalance service on compute", "when": "compute_irqbalance_disabled|bool", "service": {"name": "irqbalance.service", "state": "stopped", "enabled": false}}], "deploy_steps_tasks": [{"name": "install systemd-container for a greenfield", "when": ["step|int == 1", "not check_nova_instances_result.stat.exists"], "package": {"name": "systemd-container", "state": "present"}}, {"name": "validate nova-compute container state", "containers.podman.podman_container_info": {"name": "nova_compute"}, "register": "nova_compute_infos", "failed_when": ["nova_compute_infos.containers.0.Healthcheck.Status is defined", "'healthy' not in nova_compute_infos.containers.0.Healthcheck.Status"], "retries": 10, "delay": 30, "tags": ["opendev-validation", "opendev-validation-nova"], "when": ["container_cli == 'podman'", "not container_healthcheck_disabled", "step|int == 6"]}, {"name": "manage PMEM namespaces for vPMEM", "include_role": {"name": "tripleo_nvdimm"}, "vars": {"tripleo_nvdimm_pmem_namespaces": ""}, "when": ["step|int == 1", "tripleo_nvdimm_pmem_namespaces != ''"]}, {"name": "manage OS version 9 specific sysctls", "when": ["step|int == 1", "ansible_facts['os_family'] == 'RedHat'", "ansible_facts['distribution_major_version'] is version('9', '==')"], "block": [{"name": "Enable post-copy by setting unprivileged_userfaultfd", "sysctl": {"name": "vm.unprivileged_userfaultfd", "value": 1, "sysctl_set": true, "sysctl_file": "/etc/sysctl.d/99-tripleo-postcopy.conf", "state": "present", "reload": true}}]}, {"name": "enable/disable ksm", "when": ["step|int == 1"], "block": [{"name": "is KSM enabled", "set_fact": {"compute_ksm_enabled": false}}, {"name": "disable KSM on compute", "when": "not compute_ksm_enabled|bool", "block": [{"name": "Check for ksm", "shell": "systemctl is-active ksm.service || systemctl is-enabled ksm.service", "become": true, "failed_when": false, "register": "ksm_service_check"}, {"name": "disable KSM services", "service": {"name": "{{ item }}", "state": "stopped", "enabled": false}, "with_items": ["ksm.service", "ksmtuned.service"], "when": ["ksm_service_check.rc is defined", "ksm_service_check.rc == 0"], "register": "ksmdisabled"}, {"name": "delete PageKSM after disable ksm on compute", "command": "echo 2 >/sys/kernel/mm/ksm/run", "when": ["ksm_service_check.rc is defined", "ksm_service_check.rc == 0", "ksmdisabled is changed"]}]}, {"name": "enable KSM on compute", "when": "compute_ksm_enabled|bool", "block": [{"name": "make sure package providing ksmtuned is installed (RHEL8 or CentOS8)", "package": {"name": "qemu-kvm-common", "state": "present"}, "when": ["ansible_facts['distribution_major_version'] is version('8', '==')"]}, {"name": "make sure package providing ksmtuned is installed (RHEL9 or CentOS9)", "package": {"name": "ksmtuned", "state": "present"}, "when": ["ansible_facts['distribution_major_version'] is version('9', '==')"]}, {"name": "enable ksmtunded", "service": {"name": "{{ item }}", "state": "started", "enabled": true}, "with_items": ["ksm.service", "ksmtuned.service"]}]}]}], "external_post_deploy_tasks": [{"block": [{"name": "is additonal Cell?", "set_fact": {"nova_additional_cell": false}}, {"name": "check if discover hosts is required", "when": ["not nova_additional_cell|bool", "nova_cellv2_discovery_done is not defined"], "block": [{"name": "discover via nova_manager?", "set_fact": {"nova_cellv2_discovery_delegate_host": "{{ groups['nova_manager'][0] }}", "nova_cellv2_discovery_container": "nova_manager"}, "when": ["groups['nova_manager'] is defined and (groups['nova_manager']|length>0)"]}, {"name": "discover via nova_api?", "set_fact": {"nova_cellv2_discovery_delegate_host": "{{ groups['nova_api'][0] }}", "nova_cellv2_discovery_container": "nova_api"}, "when": ["nova_cellv2_discovery_delegate_host is not defined", "groups['nova_api'] is defined and (groups['nova_api']|length>0)"]}, {"name": "Warn if no discovery host available", "fail": {"msg": "No hosts available to run nova cell_v2 host discovery."}, "ignore_errors": true, "when": ["nova_cellv2_discovery_delegate_host is not defined"]}, {"name": "Discovering nova hosts", "command": "{{ container_cli }} exec {{ nova_cellv2_discovery_container }} nova-manage cell_v2 discover_hosts --by-service", "become": true, "changed_when": false, "delegate_to": "{{ nova_cellv2_discovery_delegate_host }}", "when": ["nova_cellv2_discovery_delegate_host is defined"]}, {"set_fact": {"nova_cellv2_discovery_done": true}}]}]}], "upgrade_tasks": [{"name": "Remove openstack-nova-compute and python-nova package during upgrade", "package": {"name": ["openstack-nova-compute", "python-nova"], "state": "removed"}, "failed_when": false, "when": "step|int == 2"}, {"name": "nova_compute pre system_upgrade tasks", "tags": ["never", "system_upgrade", "system_upgrade_run", "system_upgrade_nfsmounts"], "when": ["step|int == 3", "upgrade_leapp_enabled"], "block": [{"name": "Unmount Nova NFS Share", "vars": {"nfs_backend_enable": false}, "mount": {"name": "/var/lib/nova/instances", "state": "absent", "fstype": "nfs4"}, "when": "nfs_backend_enable|bool"}]}, {"name": "nova_compute post system_upgrade tasks", "tags": ["never", "system_upgrade", "system_upgrade_run", "system_upgrade_nfsmounts"], "when": ["step|int == 5", "upgrade_leapp_enabled"], "block": [{"name": "Mount Nova NFS Share", "vars": {"nfs_backend_enable": false, "nfs_share": "", "nfs_options": "context=system_u:object_r:nfs_t:s0", "nfs_vers": "4.2"}, "mount": {"name": "/var/lib/nova/instances", "state": "mounted", "fstype": "nfs4", "src": "{{nfs_share}}", "opts": "_netdev,bg,{{nfs_options}},nfsvers={{nfs_vers}}"}, "when": "nfs_backend_enable|bool"}]}], "update_tasks": [{"name": "Remove openstack-nova-compute and python-nova package during upgrade", "package": {"name": ["openstack-nova-compute", "python-nova"], "state": "removed"}, "failed_when": false, "when": "step|int == 2"}], "post_update_tasks": [{"name": "Ensure instanceha directory is purged", "when": "step|int == 1", "become": true, "file": {"path": "/var/lib/nova/instanceha", "state": "absent"}}], "post_upgrade_tasks": [{"name": "Ensure instanceha directory is purged", "when": "step|int == 1", "become": true, "file": {"path": "/var/lib/nova/instanceha", "state": "absent"}}], "scale_tasks": [{"when": ["step|int == 1", "container_cli == 'podman'"], "tags": "down", "become": true, "environment": {"OS_CLOUD": "standalone"}, "block": [{"name": "Get nova-compute service ID", "command": "openstack compute service list --service nova-compute --column ID --column Host --format yaml", "register": "nova_compute_service_result", "delegate_to": "{{ groups['Undercloud'] | first }}", "check_mode": false, "changed_when": false}, {"name": "is additional Cell?", "set_fact": {"is_additional_cell": false}}, {"name": "Set fact for nova_compute services", "set_fact": {"nova_compute_service": "{{ nova_compute_service_result.stdout | from_yaml | selectattr('Host', 'match', ansible_facts['fqdn'] ~ '.*') | list }}"}, "delegate_to": "{{ groups['Undercloud'] | first }}", "check_mode": false}, {"name": "Check search output", "fail": {"msg": "Found multiple `{{ ansible_facts['fqdn'] }}`, which is unexpected. This means that the FQDN of the selected device to disable is either wrong or is sharing a name with another host, which is also wrong. Please correct this issue before continuing. Nova service list return data can be found here -> {{ nova_compute_service }}."}, "when": ["(nova_compute_service | length) > 1"]}, {"name": "Disable nova services", "when": ["(nova_compute_service | length) == 1"], "block": [{"name": "Disable nova-compute service", "command": "openstack compute service set {{ nova_compute_service[0].Host }} nova-compute --disable", "delegate_to": "{{ groups['Undercloud'] | first }}", "check_mode": false, "when": ["not is_additional_cell|bool"]}, {"name": "Stop nova-compute container", "service": {"name": "tripleo_nova_compute", "state": "stopped", "enabled": false}, "become": true, "register": "stop_nova_compute", "failed_when": "('msg' in stop_nova_compute and 'Could not find the requested service tripleo_nova_compute' not in stop_nova_compute.msg) or ('rc' in stop_nova_compute and stop_nova_compute.rc != 0)"}, {"name": "Delete nova-compute service", "command": "openstack compute service delete {{ nova_compute_service[0].ID }}", "delegate_to": "{{ groups['Undercloud'] | first }}", "check_mode": false}]}]}]}, "tuple:('role_data', 'service_names')": null, "tuple:('role_data', 'config_settings')": {"nova::my_ip": "%{hiera('internal_api')}", "nova::rabbit_use_ssl": "True", "nova::keystone::service_user::send_service_user_token": true, "nova::keystone::service_user::project_name": "service", "nova::keystone::service_user::password": "ksmPDM6JveiqLvoFg56EAxny1", "nova::keystone::service_user::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::keystone::service_user::region_name": "regionOne", "nova::placement::project_name": "service", "nova::placement::password": "H4Zat7IGHRAxn3vftDX6FGvMZ", "nova::placement::auth_url": "https://overcloud.internalapi.ooo.test:5000", "nova::placement::region_name": "regionOne", "nova::placement::valid_interfaces": "internal", "nova::os_region_name": "regionOne", "nova::logging::debug": true, "nova::purge_config": false, "nova::network::neutron::project_name": "service", "nova::network::neutron::username": "neutron", "nova::network::neutron::region_name": "regionOne", "nova::dhcp_domain": "", "nova::network::neutron::password": "CC70wbFRk1QjXjqZLaKBjXfHn", "nova::network::neutron::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::network::neutron::valid_interfaces": "internal", "nova::glance::valid_interfaces": "internal", "nova::rabbit_heartbeat_timeout_threshold": 60, "nova::cinder_catalog_info": "volumev3:cinderv3:internalURL", "nova::host": "%{hiera('fqdn_canonical')}", "nova::notify_on_state_change": "vm_and_task_state", "nova::notification_driver": ["noop"], "nova::notification_format": "unversioned", "nova::network::neutron::auth_type": "v3password", "nova::db::database_db_max_retries": -1, "nova::db::database_max_retries": -1, "nova::network::neutron::ovs_bridge": "br-int", "nova::compute::sync_power_state_interval": 600, "nova_is_additional_cell": false, "nova::cross_az_attach": true, "nova::cache::enabled": true, "nova::cache::tls_enabled": true, "nova::cache::backend": "dogpile.cache.pymemcache", "nova::rabbit_heartbeat_in_pthread": false, "nova::ovsdb_connection": "", "nova::compute::vcpu_pin_set": [], "nova::compute::cpu_shared_set": [], "nova::compute::cpu_dedicated_set": [], "nova::compute::reserved_host_memory": 512, "nova::compute::reserved_huge_pages": [], "nova::compute::neutron_physnets_numa_nodes_mapping": {}, "nova::compute::neutron_tunnel_numa_nodes": [], "nova::compute::resume_guests_state_on_host_boot": false, "nova::compute::libvirt::rx_queue_size": 512, "nova::compute::libvirt::tx_queue_size": 512, "nova::compute::libvirt::file_backed_memory": 0, "nova::compute::libvirt::volume_use_multipath": false, "nova::compute::libvirt::hw_machine_type": "x86_64=pc-q35-rhel9.0.0", "nova::compute::mkisofs_cmd": "mkisofs", "compute_enable_ksm": false, "nova::compute::rbd::libvirt_images_rbd_pool": "vms", "tripleo::profile::base::nova::compute::nova_nfs_enabled": false, "nfs_backend_enable": false, "nfs_share": "", "nfs_options": "context=system_u:object_r:nfs_t:s0", "nfs_vers": "4.2", "nova::compute::libvirt::num_pcie_ports": 16, "nova::compute::libvirt::mem_stats_period_seconds": 10, "nova::compute::rbd::ephemeral_storage": false, "resume_guests_state_on_host_boot": false, "nova::cpu_allocation_ratio": 0.0, "nova::ram_allocation_ratio": 1.0, "nova::disk_allocation_ratio": 0.0, "nova::compute::max_disk_devices_to_attach": -1, "nova::compute::libvirt::pmem_namespaces": "", "nova_pmem_namespaces": "", "nova::compute::image_cache::remove_unused_original_minimum_age_seconds": 86400, "nova::compute::libvirt::swtpm_enabled": false, "nova::compute::vgpu::vgpu_types_device_addresses_mapping": {}, "compute_disable_irqbalance": false, "nova::compute::libvirt::cpu_mode": "host-model", "nova::compute::libvirt::cpu_models": [], "nova::compute::libvirt::cpu_model_extra_flags": "", "nova_compute_opt_volumes": [], "nova_compute_startup_delay": 0, "nova_compute_opt_env_vars": {}, "nova::workarounds::never_download_image_if_on_rbd": false, "nova::compute::image_type_exclude_list": [], "nova_permit_post_copy": true, "nova_permit_auto_converge": true, "nova_libvirt_max_queues": 0, "nova_glance_enable_rbd_download": false, "nova_glance_rbd_download_multistore_id": "", "nova::compute::force_raw_images": true, "nova::compute::use_cow_images": true, "nova::compute::libvirt::preallocate_images": "none", "nova::compute::image_cache::manager_interval": 2400, "nova::compute::image_cache::remove_unused_base_images": true, "nova::compute::image_cache::remove_unused_resized_minimum_age_seconds": 3600, "nova::compute::image_cache::precache_concurrency": 1, "nova::compute::provider::custom_inventories": {}, "nova::compute::rbd::libvirt_images_rbd_glance_copy_poll_interval": 15, "nova::compute::rbd::libvirt_images_rbd_glance_copy_timeout": 600, "ContainerNovaComputeImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-compute:17.1", "ContainerNovaLibvirtConfigImage": "registry.redhat.io/rhosp-rhel9/openstack-nova-libvirt:17.1", "nova::compute::libvirt::manage_libvirt_services": false, "nova::compute::pci::passthrough": "[{\"devname\": \"dummy-dev\", \"physical_network\": \"dummy_sriov_net\"}]", "nova::compute::libvirt::migration_support": false, "tripleo::profile::base::nova::migration::client::nova_compute_enabled": true, "tripleo::profile::base::nova::migration::client::ssh_private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtEHfMqe/CUfsijQpoJHgkGtXu7olMqGTlbiR3w/aOmycSlLy\nDRC1GFuaSXaRmZzDEoClHGQeMcc2eNzi/edgcjd0Zn9zUnCmq7wbcDYgInwnNgcw\na7jw/sCw7FgfL5rjJus0dGSwcG+aaStUsjcF0f0KwTCnMYPOBQmoDmfcLM/KXwEg\n6xPvoUeXWLmuiYUhX8Ov6/pzQ8qhjwh5QxMGSmDqkE9k+g1AHUAVCnx1H7ABfLgW\nDg5nDt+z1POuw62ttzTjIiTJkOPGzyTgPSUM8G+zcvVUeg6R0tsUj5SdIdpx6ldR\nibjRUo6d+bOyBnK/CLKaXVW0wFMUlqf5vOKEHwIDAQABAoIBAA7SJbRoB3SPZdr9\nK/kIVYL6WHXWec1+bICQcun4xzQrjvQrerxpRcKRcY+dwJ31PjLMwgaiErt8CjYI\nz18aAZuShK8SxpweKA4Rwt3IjX7YX275S1loFOSVWlasbi//V7Lw/tidGMa93l/M\nUMPwczmPSS2WPwD6uds/LoK0tm02qy9xv/1v36xGZTClKbGJm7Gr8DqRBfSrF/Xi\ncUWswxbiEfwVlLQ3KUjH8A/nqoOwDFlkmyw4I1t16Nto6fvsywSS80z2+PZUZyN1\nwcb+Kw7UiJhSiM3E58ecFKloLb0pnuO4EIDqIP3+uoOrvjM4hUHGtDpjcclz7fY8\ntzVi3OECgYEAzz+tx3/tMFbuSjivurgoXFVDDIOAZG3vWY2oeA30j22uUhfXnS4Q\npHRLfxh01avP21fb/N8dlipl/ZY97OsdMghLa8LVYKKs+dpJObiO9COXqOCgf9uH\nOuA8HUiet1vrO8x000zlVRl08jZEhHd87qcgA8jXFhzVQYkuk2aB4D8CgYEA3qjL\ndECE1S9QLmivvcRs+512yPaiXjB6EpVABiSPha2JrFh3vjWzItMb1dQEa3w12kyg\nZpVIfUvjkdaVAU0jJRTX2LivQw1Eb+TIBKTxN0bpsbzGLhZV22y5gqTqlWVtalwr\nsXoCyNMi6TJg3Dm4Jh662f2HI566+NZ5f1J0ZCECgYATYCGwCA61uTrX54rOSw9C\n6pm1HsovGXQKDEtWxf86NYfZlu8Ke9cEePbh7/hkTYbGO+WOM5HBiz8zwQsTDjwR\nhGMAtRtbyHmhdj3oTo/W1UkFedKngW+YGoSkPDjSbZ6FXa0CYCsElLexOU+Yjs84\nRJmioPEy2Qk68kdPcoLr4wKBgDedzgQhwYxGny6Yc51hHR/Ue0FDaddBIJzXrwBZ\nzYfEiykDiBV48F/4f3Fv3hLDGeiHT2GQVGUHubZJ9To3/Flc1XWK3CxRehDBAr02\nPhJioikFUDNe3N9MTyI082uRxDzW/Qr9wQhMxOGe9dbMjBTphB7DVygsk+NoHz0Q\naYZBAoGBAIcWC5mh7BK88xWwH7lTd29gBC0sMfV+4L8/NWQvnxg1omfjfJ32e5sd\n5QqKigi6MtTJwDtXO1EXLxYOjX1zc2jBRYP5NZioxZd0Bc0UvBowcSP5ib5fK4LM\nZzEajZhutEfz1KycMybzQtyzmurMKTVPoLRxIftwg9KKYK7tdeMg\n-----END RSA PRIVATE KEY-----\n", "tripleo::profile::base::nova::migration::client::ssh_port": 2022, "nova::compute::rbd::libvirt_images_rbd_glance_store_name": "default_backend", "nova::compute::rbd::libvirt_images_rbd_ceph_conf": "/etc/ceph/ceph.conf", "nova::compute::rbd::libvirt_rbd_user": "openstack", "nova::compute::rbd::rbd_keyring": "client.openstack", "tripleo::profile::base::nova::compute::cinder_nfs_backend": false, "rbd_persistent_storage": false, "rbd_disk_cachemodes": ["network=writeback"], "nova::cinder::username": "cinder", "nova::cinder::auth_type": "v3password", "nova::cinder::project_name": "service", "nova::cinder::password": "FEja3ueKnb89UNaV9R0nCV9rF", "nova::cinder::auth_url": "https://overcloud.internalapi.ooo.test:5000/v3", "nova::cinder::region_name": "regionOne", "nova::compute::rbd::libvirt_rbd_secret_key": "AQCtBi1qAAAAABAAlMbuGDpAuKfBXDWSJGSRjw==", "nova::compute::rbd::libvirt_rbd_secret_uuid": "f72fc8e7-c471-4878-af38-3eab533fd252", "nova::compute::instance_usage_audit": true, "nova::compute::instance_usage_audit_period": "hour", "nova::compute::consecutive_build_service_disable_threshold": "10", "nova::compute::live_migration_wait_for_vif_plug": true, "nova::migration::libvirt::live_migration_permit_post_copy": true, "nova::migration::libvirt::live_migration_permit_auto_converge": true, "nova::migration::libvirt::live_migration_tunnelled": false, "nova::compute::vncserver_proxyclient_address": "%{hiera('internal_api')}", "nova::compute::vncproxy_host": "standalone.ooo.test", "nova::vncproxy::common::vncproxy_protocol": "https", "nova::vncproxy::common::vncproxy_host": "standalone.ooo.test", "nova::vncproxy::common::vncproxy_port": "13080", "nova::compute::verify_glance_signatures": false, "nova::workarounds::disable_compute_service_check_for_ffu": false}, "OS::stack_id": "arn:openstack:heat::admin:stacks/standalone-StandaloneServiceChain-crsk2ghna6xy-ServiceChain-wkba2mh25u55-38-qqzikclnisao/29b445dd-0851-40f9-be5c-7a8006ce682a"}


