From 2f0240d267e93b9e0dd26ae0a505dd91663277aa Mon Sep 17 00:00:00 2001 From: anolis-bot Date: Thu, 10 Nov 2022 14:29:33 +0800 Subject: [PATCH] update to resource-agents-4.9.0-29.el8_7.2 Signed-off-by: anolis-bot --- 7-gcp-bundled.patch | 25 +- ...agents-set-domain-parameters-default.patch | 55 ++ ...k-agents-warn-when-openstackcli-slow.patch | 282 ++++++ ...48-bz1949114-update-openstack-agents.patch | 770 +++++++++++++++ ...147-bz1949114-openstack-agents-fixes.patch | 72 ++ bz1908148-openstack-info-fix-bashism.patch | 26 + ...m-1-fix-uuid-label-device-whitespace.patch | 44 + ...em-2-improve-uuid-label-device-logic.patch | 38 + ...> bz2064342-1-IPsrcaddr-dhcp-warning.patch | 0 ...rcaddr-error-message-route-not-found.patch | 0 ...z2064342-3-IPsrcaddr-fix-indentation.patch | 0 ...patch => bz2064342-4-IPsrcaddr-fixes.patch | 0 bz2072043-LVM-activate-fix-fence-issue.patch | 102 ++ ...lockd-fail-when-use_lvmlockd-not-set.patch | 25 + ...process-to-root-cgroup-if-rt-enabled.patch | 0 ...-move-ip-add-interface-label-support.patch | 82 ++ bz2103370-ocf-tester-1-update.patch | 39 + ...remove-deprecated-lrmd-lrmadmin-code.patch | 166 ++++ ...ovsmonitor-pgsql-fix-attrd_updater-q.patch | 75 ++ bz2130986-azure-events-az-new-ra.patch | 903 ++++++++++++++++++ ...oto-metric-scope-default-route-fixes.patch | 0 dist | 2 +- download | 1 + pyparsing-2.4.7-py2.py3-none-any.whl | Bin 0 -> 67842 bytes resource-agents.spec | 157 ++- 25 files changed, 2819 insertions(+), 45 deletions(-) create mode 100644 bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-set-domain-parameters-default.patch create mode 100644 bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-warn-when-openstackcli-slow.patch create mode 100644 bz1908146-bz1908147-bz1908148-bz1949114-update-openstack-agents.patch create mode 100644 bz1908146-bz1908147-bz1949114-openstack-agents-fixes.patch create mode 100644 bz1908148-openstack-info-fix-bashism.patch create mode 100644 bz2049414-Filesystem-1-fix-uuid-label-device-whitespace.patch create mode 100644 bz2049414-Filesystem-2-improve-uuid-label-device-logic.patch rename bz1654862-1-IPsrcaddr-dhcp-warning.patch => bz2064342-1-IPsrcaddr-dhcp-warning.patch (100%) rename bz1654862-2-IPsrcaddr-error-message-route-not-found.patch => bz2064342-2-IPsrcaddr-error-message-route-not-found.patch (100%) rename bz1654862-3-IPsrcaddr-fix-indentation.patch => bz2064342-3-IPsrcaddr-fix-indentation.patch (100%) rename bz1654862-4-IPsrcaddr-fixes.patch => bz2064342-4-IPsrcaddr-fixes.patch (100%) create mode 100644 bz2072043-LVM-activate-fix-fence-issue.patch create mode 100644 bz2086889-lvmlockd-fail-when-use_lvmlockd-not-set.patch rename bz2092262-CTDB-move-process-to-root-cgroup-if-rt-enabled.patch => bz2090370-CTDB-move-process-to-root-cgroup-if-rt-enabled.patch (100%) create mode 100644 bz2093214-aws-vpc-move-ip-add-interface-label-support.patch create mode 100644 bz2103370-ocf-tester-1-update.patch create mode 100644 bz2103370-ocf-tester-2-remove-deprecated-lrmd-lrmadmin-code.patch create mode 100644 bz2116941-ethmonitor-ovsmonitor-pgsql-fix-attrd_updater-q.patch create mode 100644 bz2130986-azure-events-az-new-ra.patch rename bz2134539-IPsrcaddr-proto-metric-scope-default-route-fixes.patch => bz2134536-IPsrcaddr-proto-metric-scope-default-route-fixes.patch (100%) create mode 100644 pyparsing-2.4.7-py2.py3-none-any.whl diff --git a/7-gcp-bundled.patch b/7-gcp-bundled.patch index b341dac..4e983ef 100644 --- a/7-gcp-bundled.patch +++ b/7-gcp-bundled.patch @@ -1,6 +1,6 @@ -diff -uNr a/heartbeat/gcp-vpc-move-ip.in b/heartbeat/gcp-vpc-move-ip.in ---- a/heartbeat/gcp-vpc-move-ip.in 2019-04-05 09:20:26.164739897 +0200 -+++ b/heartbeat/gcp-vpc-move-ip.in 2019-04-05 09:21:01.331139742 +0200 +diff --color -uNr a/heartbeat/gcp-vpc-move-ip.in b/heartbeat/gcp-vpc-move-ip.in +--- a/heartbeat/gcp-vpc-move-ip.in 2022-06-16 09:45:21.419090782 +0200 ++++ b/heartbeat/gcp-vpc-move-ip.in 2022-06-16 10:11:22.978648598 +0200 @@ -36,7 +36,7 @@ . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs @@ -10,9 +10,9 @@ diff -uNr a/heartbeat/gcp-vpc-move-ip.in b/heartbeat/gcp-vpc-move-ip.in OCF_RESKEY_configuration_default="default" OCF_RESKEY_vpc_network_default="default" OCF_RESKEY_interface_default="eth0" -diff -uNr a/heartbeat/gcp-vpc-move-route.in b/heartbeat/gcp-vpc-move-route.in ---- a/heartbeat/gcp-vpc-move-route.in 2019-04-05 09:20:26.180739624 +0200 -+++ b/heartbeat/gcp-vpc-move-route.in 2019-04-05 09:22:28.648649593 +0200 +diff --color -uNr a/heartbeat/gcp-vpc-move-route.in b/heartbeat/gcp-vpc-move-route.in +--- a/heartbeat/gcp-vpc-move-route.in 2022-06-16 09:45:21.420090788 +0200 ++++ b/heartbeat/gcp-vpc-move-route.in 2022-06-16 10:11:22.978648598 +0200 @@ -45,6 +45,7 @@ from ocf import * @@ -20,4 +20,15 @@ diff -uNr a/heartbeat/gcp-vpc-move-route.in b/heartbeat/gcp-vpc-move-route.in + sys.path.insert(0, '/usr/lib/resource-agents/bundled/gcp') import googleapiclient.discovery import pyroute2 - except ImportError: + try: +diff --color -uNr a/heartbeat/gcp-vpc-move-vip.in b/heartbeat/gcp-vpc-move-vip.in +--- a/heartbeat/gcp-vpc-move-vip.in 2022-06-16 09:45:21.420090788 +0200 ++++ b/heartbeat/gcp-vpc-move-vip.in 2022-06-16 10:11:22.979648603 +0200 +@@ -29,6 +29,7 @@ + from ocf import * + + try: ++ sys.path.insert(0, '/usr/lib/resource-agents/bundled/gcp') + import googleapiclient.discovery + try: + from google.oauth2.service_account import Credentials as ServiceAccountCredentials diff --git a/bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-set-domain-parameters-default.patch b/bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-set-domain-parameters-default.patch new file mode 100644 index 0000000..8ee70e5 --- /dev/null +++ b/bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-set-domain-parameters-default.patch @@ -0,0 +1,55 @@ +From bb5cfa172ca58cd8adcedcaca92bde54d0645661 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Thu, 14 Jul 2022 10:55:19 +0200 +Subject: [PATCH] openstack-agents: set domain parameter's default to Default + and fix missing parameter name in ocf_exit_reason + +--- + heartbeat/openstack-common.sh | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/heartbeat/openstack-common.sh b/heartbeat/openstack-common.sh +index b6eec09c..14d290bd 100644 +--- a/heartbeat/openstack-common.sh ++++ b/heartbeat/openstack-common.sh +@@ -1,6 +1,10 @@ ++OCF_RESKEY_user_domain_name_default="Default" ++OCF_RESKEY_project_domain_name_default="Default" + OCF_RESKEY_openstackcli_default="/usr/bin/openstack" + OCF_RESKEY_insecure_default="false" + ++: ${OCF_RESKEY_user_domain_name=${OCF_RESKEY_user_domain_name_default}} ++: ${OCF_RESKEY_project_domain_name=${OCF_RESKEY_project_domain_name_default}} + : ${OCF_RESKEY_openstackcli=${OCF_RESKEY_openstackcli_default}} + : ${OCF_RESKEY_insecure=${OCF_RESKEY_insecure_default}} + +@@ -64,7 +68,7 @@ Keystone Project. + Keystone User Domain Name. + + Keystone User Domain Name +- ++ + + + +@@ -72,7 +76,7 @@ Keystone User Domain Name. + Keystone Project Domain Name. + + Keystone Project Domain Name +- ++ + + + +@@ -133,7 +137,7 @@ get_config() { + exit $OCF_ERR_CONFIGURED + fi + if [ -z "$OCF_RESKEY_project_domain_name" ]; then +- ocf_exit_reason " not set" ++ ocf_exit_reason "project_domain_name not set" + exit $OCF_ERR_CONFIGURED + fi + +-- +2.36.1 + diff --git a/bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-warn-when-openstackcli-slow.patch b/bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-warn-when-openstackcli-slow.patch new file mode 100644 index 0000000..3f8bf0c --- /dev/null +++ b/bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-warn-when-openstackcli-slow.patch @@ -0,0 +1,282 @@ +From ebea4c3620261c529cad908c0e52064df84b0c61 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Mon, 11 Jul 2022 10:28:11 +0200 +Subject: [PATCH] openstack-agents: warn when openstackcli is slow + +--- + heartbeat/openstack-cinder-volume | 19 +++++++++++-------- + heartbeat/openstack-common.sh | 22 ++++++++++++++++++++++ + heartbeat/openstack-floating-ip | 17 ++++++++++------- + heartbeat/openstack-info.in | 20 ++++++++++---------- + heartbeat/openstack-virtual-ip | 20 ++++++++++---------- + 5 files changed, 63 insertions(+), 35 deletions(-) + +diff --git a/heartbeat/openstack-cinder-volume b/heartbeat/openstack-cinder-volume +index 19bf04faf..116442c41 100755 +--- a/heartbeat/openstack-cinder-volume ++++ b/heartbeat/openstack-cinder-volume +@@ -113,11 +113,14 @@ _get_node_id() { + } + + osvol_validate() { ++ local result ++ + check_binary "$OCF_RESKEY_openstackcli" + + get_config + +- if ! $OCF_RESKEY_openstackcli volume list|grep -q $OCF_RESKEY_volume_id ; then ++ result=$(run_openstackcli "volume list") ++ if ! echo "$result" | grep -q $OCF_RESKEY_volume_id; then + ocf_exit_reason "volume-id $OCF_RESKEY_volume_id not found" + return $OCF_ERR_CONFIGURED + fi +@@ -156,17 +159,17 @@ osvol_monitor() { + # Is the volue attached? + # We use the API + # +- result=$($OCF_RESKEY_openstackcli volume show \ ++ result=$(run_openstackcli "volume show \ + --column status \ + --column attachments \ + --format value \ +- $OCF_RESKEY_volume_id) ++ $OCF_RESKEY_volume_id") + +- if echo "$result" | grep -q available ; then ++ if echo "$result" | grep -q available; then + ocf_log warn "$OCF_RESKEY_volume_id is not attached to any instance" + return $OCF_NOT_RUNNING + else +- export attached_server_id=$(echo $result|head -n1| ++ export attached_server_id=$(echo "$result"|head -n1| + grep -P -o "'server_id': '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}'"| + grep -P -o "[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}") + ocf_log info "$OCF_RESKEY_volume_id is attached to instance $attached_server_id" +@@ -199,7 +202,7 @@ osvol_stop() { + # + # Detach the volume + # +- if ! $OCF_RESKEY_openstackcli server remove volume $node_id $OCF_RESKEY_volume_id ; then ++ if ! run_openstackcli "server remove volume $node_id $OCF_RESKEY_volume_id"; then + ocf_log error "Couldn't remove volume $OCF_RESKEY_volume_id from instance $node_id" + return $OCF_ERR_GENERIC + fi +@@ -225,7 +228,7 @@ osvol_start() { + # TODO: make it optional in case multi-attachment is allowed by Cinder + # + if [ ! -z $attached_server_id ] ; then +- if ! $OCF_RESKEY_openstackcli server remove volume $attached_server_id $OCF_RESKEY_volume_id ; then ++ if ! run_openstackcli "server remove volume $attached_server_id $OCF_RESKEY_volume_id"; then + ocf_log error "Couldn't remove volume $OCF_RESKEY_volume_id from instance $attached_server_id" + return $OCF_ERR_GENERIC + fi +@@ -238,7 +241,7 @@ osvol_start() { + # + # Attach the volume + # +- $OCF_RESKEY_openstackcli server add volume $node_id $OCF_RESKEY_volume_id ++ run_openstackcli "server add volume $node_id $OCF_RESKEY_volume_id" + if [ $? != $OCF_SUCCESS ]; then + ocf_log error "Couldn't add volume $OCF_RESKEY_volume_id to instance $node_id" + return $OCF_ERR_GENERIC +diff --git a/heartbeat/openstack-common.sh b/heartbeat/openstack-common.sh +index 4763c90db..b6eec09c2 100644 +--- a/heartbeat/openstack-common.sh ++++ b/heartbeat/openstack-common.sh +@@ -145,3 +145,25 @@ get_config() { + OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --os-project-domain-name $OCF_RESKEY_project_domain_name" + fi + } ++ ++run_openstackcli() { ++ local cmd="${OCF_RESKEY_openstackcli} $1" ++ local result ++ local rc ++ local start_time=$(date +%s) ++ local end_time ++ local elapsed_time ++ ++ result=$($cmd) ++ rc=$? ++ end_time=$(date +%s) ++ elapsed_time=$(expr $end_time - $start_time) ++ ++ if [ $elapsed_time -gt 20 ]; then ++ ocf_log warn "$cmd took ${elapsed_time}s to complete" ++ fi ++ ++ echo "$result" ++ ++ return $rc ++} +diff --git a/heartbeat/openstack-floating-ip b/heartbeat/openstack-floating-ip +index 6e2895654..7317f19a8 100755 +--- a/heartbeat/openstack-floating-ip ++++ b/heartbeat/openstack-floating-ip +@@ -101,11 +101,14 @@ END + } + + osflip_validate() { ++ local result ++ + check_binary "$OCF_RESKEY_openstackcli" + + get_config + +- if ! $OCF_RESKEY_openstackcli floating ip list|grep -q $OCF_RESKEY_ip_id ; then ++ result=$(run_openstackcli "floating ip list") ++ if ! echo "$result" | grep -q $OCF_RESKEY_ip_id; then + ocf_exit_reason "ip-id $OCF_RESKEY_ip_id not found" + return $OCF_ERR_CONFIGURED + fi +@@ -132,14 +135,14 @@ osflip_monitor() { + | awk '{gsub("[^ ]*:", "");print}') + + # Is the IP active and attached? +- result=$($OCF_RESKEY_openstackcli floating ip show \ ++ result=$(run_openstackcli "floating ip show \ + --column port_id --column floating_ip_address \ + --format yaml \ +- $OCF_RESKEY_ip_id) ++ $OCF_RESKEY_ip_id") + + for port in $node_port_ids ; do +- if echo $result | grep -q $port ; then +- floating_ip=$(echo $result | awk '/floating_ip_address/ {print $2}') ++ if echo "$result" | grep -q $port ; then ++ floating_ip=$(echo "$result" | awk '/floating_ip_address/ {print $2}') + ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -S status -n openstack_floating_ip -v $floating_ip + + return $OCF_SUCCESS +@@ -160,7 +163,7 @@ osflip_stop() { + return $OCF_SUCCESS + fi + +- if ! $OCF_RESKEY_openstackcli floating ip unset --port $OCF_RESKEY_ip_id ; then ++ if ! run_openstackcli "floating ip unset --port $OCF_RESKEY_ip_id"; then + return $OCF_ERR_GENERIC + fi + +@@ -194,7 +197,7 @@ osflip_start() { + + ocf_log info "Moving IP address $OCF_RESKEY_ip_id to port ID $node_port_id" + +- $OCF_RESKEY_openstackcli floating ip set --port $node_port_id $OCF_RESKEY_ip_id ++ run_openstackcli "floating ip set --port $node_port_id $OCF_RESKEY_ip_id" + if [ $? != $OCF_SUCCESS ]; then + ocf_log error "$OCF_RESKEY_ip_id Cannot be set to port $node_port_id" + return $OCF_ERR_GENERIC +diff --git a/heartbeat/openstack-info.in b/heartbeat/openstack-info.in +index f3a59fc7a..6502f1df1 100755 +--- a/heartbeat/openstack-info.in ++++ b/heartbeat/openstack-info.in +@@ -119,9 +119,7 @@ END + ####################################################################### + + OSInfoStats() { +- local result + local value +- local node + local node_id + + get_config +@@ -141,31 +139,33 @@ OSInfoStats() { + ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_id -v "$node_id" + + # Nova data: flavor +- value=$($OCF_RESKEY_openstackcli server show \ ++ value=$(run_openstackcli "server show \ + --format value \ + --column flavor \ +- $node_id) ++ $node_id") + + ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_flavor -v "$value" + + # Nova data: availability zone +- value=$($OCF_RESKEY_openstackcli server show \ ++ value=$(run_openstackcli "server show \ + --format value \ + --column OS-EXT-AZ:availability_zone \ +- $node_id) ++ $node_id") + + ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_az -v "$value" + + # Network data: ports + value="" +- for port_id in $($OCF_RESKEY_openstackcli port list \ ++ for port_id in $(run_openstackcli "port list \ + --format value \ + --column id \ +- --server $node_id); do +- subnet_id=$($OCF_RESKEY_openstackcli port show \ ++ --server $node_id"); do ++ subnet_result=$(run_openstackcli "port show \ + --format json \ + --column fixed_ips \ +- ${port_id} | grep -P '\"subnet_id\": \".*\",$' | ++ ${port_id}") ++ subnet_id=$(echo "$subnet_result" | ++ grep -P '\"subnet_id\": \".*\",$' | + grep -P -o '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}') + value="${value}${subnet_id}:${port_id}," + done +diff --git a/heartbeat/openstack-virtual-ip b/heartbeat/openstack-virtual-ip +index c654d980a..361357d55 100755 +--- a/heartbeat/openstack-virtual-ip ++++ b/heartbeat/openstack-virtual-ip +@@ -132,11 +132,11 @@ osvip_monitor() { + + node_port_id=$(osvip_port_id) + +- result=$($OCF_RESKEY_openstackcli port show \ ++ result=$(run_openstackcli "port show \ + --format value \ + --column allowed_address_pairs \ +- ${node_port_id}) +- if echo $result | grep -q "$OCF_RESKEY_ip"; then ++ ${node_port_id}") ++ if echo "$result" | grep -q "$OCF_RESKEY_ip"; then + ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -S status -n openstack_virtual_ip -v $OCF_RESKEY_ip + + return $OCF_SUCCESS +@@ -158,20 +158,20 @@ osvip_stop() { + return $OCF_SUCCESS + fi + +- mac_address=$($OCF_RESKEY_openstackcli port show \ ++ mac_address=$(run_openstackcli "port show \ + --format value \ + --column mac_address \ +- $node_port_id) +- echo ${mac_address} | grep -q -P "^([0-9a-f]{2}:){5}[0-9a-f]{2}$" ++ $node_port_id") ++ echo "${mac_address}" | grep -q -P "^([0-9a-f]{2}:){5}[0-9a-f]{2}$" + if [ $? -ne 0 ]; then + ocf_log error "MAC address '${mac_address}' is not valid." + return $OCF_ERR_GENERIC + fi + +- if ! $OCF_RESKEY_openstackcli port unset \ ++ if ! run_openstackcli "port unset \ + --allowed-address \ + ip-address=$OCF_RESKEY_ip,mac-address=${mac_address} \ +- $node_port_id; then ++ $node_port_id"; then + return $OCF_ERR_GENERIC + fi + +@@ -196,9 +196,9 @@ osvip_start() { + + ocf_log info "Moving IP address $OCF_RESKEY_ip to port ID $node_port_id" + +- $OCF_RESKEY_openstackcli port set \ ++ run_openstackcli "port set \ + --allowed-address ip-address=$OCF_RESKEY_ip \ +- $node_port_id ++ $node_port_id" + if [ $? != $OCF_SUCCESS ]; then + ocf_log error "$OCF_RESKEY_ip Cannot be set to port $node_port_id" + return $OCF_ERR_GENERIC diff --git a/bz1908146-bz1908147-bz1908148-bz1949114-update-openstack-agents.patch b/bz1908146-bz1908147-bz1908148-bz1949114-update-openstack-agents.patch new file mode 100644 index 0000000..7b1a6e8 --- /dev/null +++ b/bz1908146-bz1908147-bz1908148-bz1949114-update-openstack-agents.patch @@ -0,0 +1,770 @@ +diff --color -uNr a/heartbeat/Makefile.am b/heartbeat/Makefile.am +--- a/heartbeat/Makefile.am 2022-03-15 16:14:29.355209012 +0100 ++++ b/heartbeat/Makefile.am 2022-03-15 16:18:35.917048467 +0100 +@@ -217,6 +217,7 @@ + lvm-clvm.sh \ + lvm-plain.sh \ + lvm-tag.sh \ ++ openstack-common.sh \ + ora-common.sh \ + mysql-common.sh \ + nfsserver-redhat.sh \ +diff --color -uNr a/heartbeat/openstack-cinder-volume b/heartbeat/openstack-cinder-volume +--- a/heartbeat/openstack-cinder-volume 2022-03-15 16:14:29.370209063 +0100 ++++ b/heartbeat/openstack-cinder-volume 2022-03-15 16:17:36.231840008 +0100 +@@ -34,11 +34,11 @@ + : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} + . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + ++. ${OCF_FUNCTIONS_DIR}/openstack-common.sh ++ + # Defaults +-OCF_RESKEY_openstackcli_default="/usr/bin/openstack" + OCF_RESKEY_volume_local_check_default="true" + +-: ${OCF_RESKEY_openstackcli=${OCF_RESKEY_openstackcli_default}} + : ${OCF_RESKEY_volume_local_check=${OCF_RESKEY_volume_local_check_default}} + + ####################################################################### +@@ -68,14 +68,11 @@ + Attach a cinder volume + + +- +- +-Path to command line tools for openstack. +- +-Path to Openstack CLI tool +- +- ++END + ++common_meta_data ++ ++cat < + + This option allows the cluster to monitor the cinder volume presence without +@@ -85,28 +82,19 @@ + + + +- +- +-Valid Openstack credentials as openrc file from api_access/openrc. +- +-openrc file +- +- +- + + +-Cinder volume identifier to use to attach the bloc storage. ++Cinder volume identifier to use to attach the block storage. + + Volume ID + + +- + + + + + +- ++ + + + +@@ -127,17 +115,7 @@ + osvol_validate() { + check_binary "$OCF_RESKEY_openstackcli" + +- if [ -z "$OCF_RESKEY_openrc" ]; then +- ocf_exit_reason "openrc parameter not set" +- return $OCF_ERR_CONFIGURED +- fi +- +- if [ ! -f "$OCF_RESKEY_openrc" ] ; then +- ocf_exit_reason "openrc file not found" +- return $OCF_ERR_CONFIGURED +- fi +- +- . $OCF_RESKEY_openrc ++ get_config + + if ! $OCF_RESKEY_openstackcli volume list|grep -q $OCF_RESKEY_volume_id ; then + ocf_exit_reason "volume-id $OCF_RESKEY_volume_id not found" +diff --color -uNr a/heartbeat/openstack-common.sh b/heartbeat/openstack-common.sh +--- a/heartbeat/openstack-common.sh 1970-01-01 01:00:00.000000000 +0100 ++++ b/heartbeat/openstack-common.sh 2022-03-15 16:17:36.232840011 +0100 +@@ -0,0 +1,147 @@ ++OCF_RESKEY_openstackcli_default="/usr/bin/openstack" ++OCF_RESKEY_insecure_default="false" ++ ++: ${OCF_RESKEY_openstackcli=${OCF_RESKEY_openstackcli_default}} ++: ${OCF_RESKEY_insecure=${OCF_RESKEY_insecure_default}} ++ ++if ocf_is_true "${OCF_RESKEY_insecure}"; then ++ OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --insecure" ++fi ++ ++common_meta_data() { ++ cat < ++ ++Openstack cloud (from ~/.config/openstack/clouds.yaml or /etc/openstack/clouds.yaml). ++ ++Cloud from clouds.yaml ++ ++ ++ ++ ++ ++Openstack credentials as openrc file from api_access/openrc. ++ ++openrc file ++ ++ ++ ++ ++ ++Keystone Auth URL ++ ++Keystone Auth URL ++ ++ ++ ++ ++ ++Username. ++ ++Username ++ ++ ++ ++ ++ ++Password. ++ ++Password ++ ++ ++ ++ ++ ++Keystone Project. ++ ++Keystone Project ++ ++ ++ ++ ++ ++Keystone User Domain Name. ++ ++Keystone User Domain Name ++ ++ ++ ++ ++ ++Keystone Project Domain Name. ++ ++Keystone Project Domain Name ++ ++ ++ ++ ++ ++Path to command line tools for openstack. ++ ++Path to Openstack CLI tool ++ ++ ++ ++ ++ ++Allow insecure connections ++ ++Allow insecure connections ++ ++ ++END ++} ++ ++get_config() { ++ if [ -n "$OCF_RESKEY_cloud" ]; then ++ TILDE=$(echo ~) ++ clouds_yaml="$TILDE/.config/openstack/clouds.yaml" ++ if [ ! -f "$clouds_yaml" ]; then ++ clouds_yaml="/etc/openstack/clouds.yaml" ++ fi ++ if [ ! -f "$clouds_yaml" ]; then ++ ocf_exit_reason "~/.config/openstack/clouds.yaml and /etc/openstack/clouds.yaml does not exist" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --os-cloud $OCF_RESKEY_cloud" ++ elif [ -n "$OCF_RESKEY_openrc" ]; then ++ if [ ! -f "$OCF_RESKEY_openrc" ]; then ++ ocf_exit_reason "$OCF_RESKEY_openrc does not exist" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ . $OCF_RESKEY_openrc ++ else ++ if [ -z "$OCF_RESKEY_auth_url" ]; then ++ ocf_exit_reason "auth_url not set" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ if [ -z "$OCF_RESKEY_username" ]; then ++ ocf_exit_reason "username not set" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ if [ -z "$OCF_RESKEY_password" ]; then ++ ocf_exit_reason "password not set" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ if [ -z "$OCF_RESKEY_project_name" ]; then ++ ocf_exit_reason "project_name not set" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ if [ -z "$OCF_RESKEY_user_domain_name" ]; then ++ ocf_exit_reason "user_domain_name not set" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ if [ -z "$OCF_RESKEY_project_domain_name" ]; then ++ ocf_exit_reason " not set" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ ++ OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --os-auth-url $OCF_RESKEY_auth_url" ++ OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --os-username $OCF_RESKEY_username" ++ OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --os-password $OCF_RESKEY_password" ++ OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --os-project-name $OCF_RESKEY_project_name" ++ OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --os-user-domain-name $OCF_RESKEY_user_domain_name" ++ OCF_RESKEY_openstackcli="${OCF_RESKEY_openstackcli} --os-project-domain-name $OCF_RESKEY_project_domain_name" ++ fi ++} +diff --color -uNr a/heartbeat/openstack-floating-ip b/heartbeat/openstack-floating-ip +--- a/heartbeat/openstack-floating-ip 2022-03-15 16:14:29.370209063 +0100 ++++ b/heartbeat/openstack-floating-ip 2022-03-15 16:17:36.233840014 +0100 +@@ -34,10 +34,9 @@ + : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} + . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + +-# Defaults +-OCF_RESKEY_openstackcli_default="/usr/bin/openstack" ++. ${OCF_FUNCTIONS_DIR}/openstack-common.sh + +-: ${OCF_RESKEY_openstackcli=${OCF_RESKEY_openstackcli_default}} ++# Defaults + + ####################################################################### + +@@ -67,22 +66,11 @@ + Move a floating IP + + +- +- +-Path to command line tools for openstack. +- +-Path to Openstack CLI tool +- +- ++END + +- +- +-Valid Openstack credentials as openrc file from api_access/openrc. +- +-openrc file +- +- ++common_meta_data + ++cat < + + Floating IP Identifier. +@@ -104,7 +92,7 @@ + + + +- ++ + + + +@@ -115,17 +103,7 @@ + osflip_validate() { + check_binary "$OCF_RESKEY_openstackcli" + +- if [ -z "$OCF_RESKEY_openrc" ]; then +- ocf_exit_reason "openrc parameter not set" +- return $OCF_ERR_CONFIGURED +- fi +- +- if [ ! -f "$OCF_RESKEY_openrc" ] ; then +- ocf_exit_reason "openrc file not found" +- return $OCF_ERR_CONFIGURED +- fi +- +- . $OCF_RESKEY_openrc ++ get_config + + if ! $OCF_RESKEY_openstackcli floating ip list|grep -q $OCF_RESKEY_ip_id ; then + ocf_exit_reason "ip-id $OCF_RESKEY_ip_id not found" +diff --color -uNr a/heartbeat/openstack-info b/heartbeat/openstack-info +--- a/heartbeat/openstack-info 1970-01-01 01:00:00.000000000 +0100 ++++ b/heartbeat/openstack-info 2022-03-15 16:17:36.234840018 +0100 +@@ -0,0 +1,270 @@ ++#!/bin/sh ++# ++# ++# OCF resource agent to set attributes from Openstack instance details. ++# It records (in the CIB) various attributes of a node ++# ++# Copyright (c) 2018 Mathieu Grzybek ++# All Rights Reserved. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of version 2 of the GNU General Public License as ++# published by the Free Software Foundation. ++# ++# This program is distributed in the hope that it would be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++# ++# Further, this software is distributed without any warranty that it is ++# free of the rightful claim of any third person regarding infringement ++# or the like. Any license provided herein, whether implied or ++# otherwise, applies only to this software file. Patent licenses, if ++# any, provided herein do not apply to combinations of this program with ++# other software, or any other product whatsoever. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write the Free Software Foundation, ++# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++# ++####################################################################### ++# Initialization: ++ ++: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} ++. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs ++ ++. ${OCF_FUNCTIONS_DIR}/openstack-common.sh ++ ++# Defaults ++OCF_RESKEY_pidfile_default="$HA_RSCTMP/OSInfo-${OCF_RESOURCE_HOSTNAME}" ++OCF_RESKEY_delay_default="0" ++OCF_RESKEY_clone_default="0" ++OCF_RESKEY_curlcli_default="/usr/bin/curl" ++OCF_RESKEY_pythoncli_default="/usr/bin/python" ++ ++: ${OCF_RESKEY_curlcli=${OCF_RESKEY_curlcli_default}} ++: ${OCF_RESKEY_pythoncli=${OCF_RESKEY_pythoncli_default}} ++: ${OCF_RESKEY_pidfile=${OCF_RESKEY_pidfile_default}} ++: ${OCF_RESKEY_delay=${OCF_RESKEY_delay_default}} ++: ${OCF_RESKEY_clone=${OCF_RESKEY_clone_default}} ++ ++####################################################################### ++ ++meta_data() { ++ cat < ++ ++ ++1.0 ++ ++ ++OCF resource agent to set attributes from Openstack instance details. ++It records (in the CIB) various attributes of a node. ++Sample output: ++ openstack_az : nova ++ openstack_flavor : c1.small ++ openstack_id : 60ac4343-5828-49b1-8aac-7c69b1417f31 ++ openstack_ports : 7960d889-9750-4160-bf41-c69a41ad72d9:96530d18-57a3-4718-af32-30f2a74c22a2,b0e55a06-bd75-468d-8baa-22cfeb65799f:a55ae917-8016-4b1e-8ffa-04311b9dc7d6 ++ ++The layout of openstack_ports is a comma-separated list of tuples "subnet_id:port_id". ++ ++Records various node attributes in the CIB ++ ++ ++END ++ ++common_meta_data ++ ++ cat < ++PID file ++PID file ++ ++ ++ ++ ++Interval to allow values to stabilize ++Dampening Delay ++ ++ ++ ++ ++ ++Path to command line cURL binary. ++ ++Path to cURL binary ++ ++ ++ ++ ++ ++Path to command line Python interpreter. ++ ++Path to Python interpreter ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++END ++} ++ ++####################################################################### ++ ++OSInfoStats() { ++ local result ++ local value ++ local node ++ local node_id ++ ++ get_config ++ ++ # Nova data: server ID ++ node_id=$($OCF_RESKEY_curlcli \ ++ -s http://169.254.169.254/openstack/latest/meta_data.json | ++ $OCF_RESKEY_pythoncli -m json.tool | ++ grep -P '\"uuid\": \".*\",$' | ++ grep -P -o '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}') ++ ++ if [ $? -ne 0 ] ; then ++ ocf_exit_reason "Cannot find server ID" ++ exit $OCF_ERR_GENERIC ++ fi ++ ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_id -v "$node_id" ++ ++ # Nova data: flavor ++ value=$($OCF_RESKEY_openstackcli server show \ ++ --format value \ ++ --column flavor \ ++ $node_id) ++ ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_flavor -v "$value" ++ ++ # Nova data: availability zone ++ value=$($OCF_RESKEY_openstackcli server show \ ++ --format value \ ++ --column OS-EXT-AZ:availability_zone \ ++ $node_id) ++ ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_az -v "$value" ++ ++ # Network data: ports ++ value="" ++ for port_id in $($OCF_RESKEY_openstackcli port list \ ++ --format value \ ++ --column id \ ++ --server $node_id); do ++ subnet_id=$($OCF_RESKEY_openstackcli port show \ ++ --format json \ ++ --column fixed_ips \ ++ ${port_id} | grep -P '\"subnet_id\": \".*\",$' | ++ grep -P -o '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}') ++ value+="${subnet_id}:${port_id}," ++ done ++ value=$(echo ${value} | sed -e 's/,$//g') ++ ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_ports -v "$value" ++ ++ if [ ! -z "$OS_REGION_NAME" ] ; then ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_region -v "$OS_REGION_NAME" ++ fi ++ ++ if [ ! -z "$OS_TENANT_ID" ] ; then ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_tenant_id -v "$OS_TENANT_ID" ++ ++ if [ ! -z "$OS_TENANT_NAME" ] ; then ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_tenant_name -v "$OS_TENANT_NAME" ++ fi ++ else ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_project_id -v "$OS_PROJECT_ID" ++ ++ if [ ! -z "$OS_PROJECT_NAME" ] ; then ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_project_name -v "$OS_PROJECT_NAME" ++ fi ++ fi ++ ++} ++ ++OSInfo_usage() { ++ cat < $OCF_RESKEY_pidfile ++ OSInfoStats ++ exit $OCF_SUCCESS ++} ++ ++OSInfo_stop() { ++ rm -f $OCF_RESKEY_pidfile ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_id ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_flavor ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_az ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_ports ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_region ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_tenant_id ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_tenant_name ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_project_id ++ ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -D -n openstack_project_name ++ exit $OCF_SUCCESS ++} ++ ++OSInfo_monitor() { ++ if [ -f "$OCF_RESKEY_pidfile" ] ; then ++ OSInfoStats ++ exit $OCF_RUNNING ++ fi ++ exit $OCF_NOT_RUNNING ++} ++ ++OSInfo_validate() { ++ check_binary "$OCF_RESKEY_curlcli" ++ check_binary "$OCF_RESKEY_openstackcli" ++ check_binary "$OCF_RESKEY_pythoncli" ++ ++ return $OCF_SUCCESS ++} ++ ++if [ $# -ne 1 ]; then ++ OSInfo_usage ++ exit $OCF_ERR_ARGS ++fi ++ ++if [ x != x${OCF_RESKEY_delay} ]; then ++ OCF_RESKEY_delay="-d ${OCF_RESKEY_delay}" ++fi ++ ++case $__OCF_ACTION in ++meta-data) meta_data ++ exit $OCF_SUCCESS ++ ;; ++start) OSInfo_validate || exit $? ++ OSInfo_start ++ ;; ++stop) OSInfo_stop ++ ;; ++monitor) OSInfo_monitor ++ ;; ++validate-all) OSInfo_validate ++ ;; ++usage|help) OSInfo_usage ++ exit $OCF_SUCCESS ++ ;; ++*) OSInfo_usage ++ exit $OCF_ERR_UNIMPLEMENTED ++ ;; ++esac ++ ++exit $? +diff --color -uNr a/heartbeat/openstack-info.in b/heartbeat/openstack-info.in +--- a/heartbeat/openstack-info.in 2022-03-15 16:14:29.370209063 +0100 ++++ b/heartbeat/openstack-info.in 2022-03-15 16:17:36.234840018 +0100 +@@ -32,16 +32,16 @@ + : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} + . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + ++. ${OCF_FUNCTIONS_DIR}/openstack-common.sh ++ + # Defaults + OCF_RESKEY_pidfile_default="$HA_RSCTMP/OSInfo-${OCF_RESOURCE_HOSTNAME}" + OCF_RESKEY_delay_default="0" + OCF_RESKEY_clone_default="0" + OCF_RESKEY_curlcli_default="/usr/bin/curl" +-OCF_RESKEY_openstackcli_default="/usr/bin/openstack" + OCF_RESKEY_pythoncli_default="@PYTHON@" + + : ${OCF_RESKEY_curlcli=${OCF_RESKEY_curlcli_default}} +-: ${OCF_RESKEY_openstackcli=${OCF_RESKEY_openstackcli_default}} + : ${OCF_RESKEY_pythoncli=${OCF_RESKEY_pythoncli_default}} + : ${OCF_RESKEY_pidfile=${OCF_RESKEY_pidfile_default}} + : ${OCF_RESKEY_delay=${OCF_RESKEY_delay_default}} +@@ -70,25 +70,23 @@ + Records various node attributes in the CIB + + ++END ++ ++common_meta_data ++ ++ cat < + PID file + PID file + + ++ + + Interval to allow values to stabilize + Dampening Delay + + + +- +- +-Valid Openstack credentials as openrc file from api_access/openrc. +- +-openrc file +- +- +- + + + Path to command line cURL binary. +@@ -97,14 +95,6 @@ + + + +- +- +-Path to command line tools for openstack. +- +-Path to Openstack CLI tool +- +- +- + + + Path to command line Python interpreter. +@@ -116,9 +106,9 @@ + + + +- +- +- ++ ++ ++ + + + +@@ -134,7 +124,7 @@ + local node + local node_id + +- . $OCF_RESKEY_openrc ++ get_config + + # Nova data: server ID + node_id=$($OCF_RESKEY_curlcli \ +@@ -244,16 +234,6 @@ + check_binary "$OCF_RESKEY_openstackcli" + check_binary "$OCF_RESKEY_pythoncli" + +- if [ -z "$OCF_RESKEY_openrc" ]; then +- ocf_exit_reason "openrc parameter not set" +- return $OCF_ERR_CONFIGURED +- fi +- +- if [ ! -f "$OCF_RESKEY_openrc" ] ; then +- ocf_exit_reason "openrc file not found" +- return $OCF_ERR_CONFIGURED +- fi +- + return $OCF_SUCCESS + } + +diff --color -uNr a/heartbeat/openstack-virtual-ip b/heartbeat/openstack-virtual-ip +--- a/heartbeat/openstack-virtual-ip 2022-03-15 16:14:29.370209063 +0100 ++++ b/heartbeat/openstack-virtual-ip 2022-03-15 16:17:36.235840021 +0100 +@@ -34,10 +34,9 @@ + : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} + . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + +-# Defaults +-OCF_RESKEY_openstackcli_default="/usr/bin/openstack" ++. ${OCF_FUNCTIONS_DIR}/openstack-common.sh + +-: ${OCF_RESKEY_openstackcli=${OCF_RESKEY_openstackcli_default}} ++# Defaults + + ####################################################################### + +@@ -68,22 +67,11 @@ + Move a virtual IP + + +- +- +-Path to command line tools for openstack. +- +-Path to Openstack CLI tool +- +- ++END + +- +- +-Valid Openstack credentials as openrc file from api_access/openrc. +- +-openrc file +- +- ++common_meta_data + ++cat < + + Virtual IP Address. +@@ -105,7 +93,7 @@ + + + +- ++ + + + +@@ -128,17 +116,7 @@ + osvip_validate() { + check_binary "$OCF_RESKEY_openstackcli" + +- if [ -z "$OCF_RESKEY_openrc" ]; then +- ocf_exit_reason "openrc parameter not set" +- return $OCF_ERR_CONFIGURED +- fi +- +- if [ ! -f "$OCF_RESKEY_openrc" ] ; then +- ocf_exit_reason "openrc file not found" +- return $OCF_ERR_CONFIGURED +- fi +- +- . $OCF_RESKEY_openrc ++ get_config + + ${HA_SBIN_DIR}/attrd_updater --query -n openstack_ports -N $(crm_node -n) > /dev/null 2>&1 + if [ $? -ne 0 ] ; then diff --git a/bz1908146-bz1908147-bz1949114-openstack-agents-fixes.patch b/bz1908146-bz1908147-bz1949114-openstack-agents-fixes.patch new file mode 100644 index 0000000..451fba7 --- /dev/null +++ b/bz1908146-bz1908147-bz1949114-openstack-agents-fixes.patch @@ -0,0 +1,72 @@ +From 64f434014bc198055478a139532c7cc133967c5d Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Fri, 8 Jul 2022 15:41:34 +0200 +Subject: [PATCH] openstack-agents: fixes + +- openstack-cinder-volume: dont do volume_local_check during start/stop-action +- openstack-floating-ip/openstack-virtual-ip: dont fail in validate() + during probe-calls +- openstack-floating-ip: fix awk only catching last id for node_port_ids +--- + heartbeat/openstack-cinder-volume | 2 +- + heartbeat/openstack-floating-ip | 4 ++-- + heartbeat/openstack-virtual-ip | 4 ++-- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/heartbeat/openstack-cinder-volume b/heartbeat/openstack-cinder-volume +index cc12e58ae..19bf04faf 100755 +--- a/heartbeat/openstack-cinder-volume ++++ b/heartbeat/openstack-cinder-volume +@@ -138,7 +138,7 @@ osvol_monitor() { + + node_id=$(_get_node_id) + +- if ocf_is_true $OCF_RESKEY_volume_local_check ; then ++ if [ "$__OCF_ACTION" = "monitor" ] && ocf_is_true $OCF_RESKEY_volume_local_check ; then + # + # Is the volue attached? + # We check the local devices +diff --git a/heartbeat/openstack-floating-ip b/heartbeat/openstack-floating-ip +index 8c135cc24..6e2895654 100755 +--- a/heartbeat/openstack-floating-ip ++++ b/heartbeat/openstack-floating-ip +@@ -111,7 +111,7 @@ osflip_validate() { + fi + + ${HA_SBIN_DIR}/attrd_updater --query -n openstack_ports -N $(crm_node -n) > /dev/null 2>&1 +- if [ $? -ne 0 ] ; then ++ if [ $? -ne 0 ] && ! ocf_is_probe; then + ocf_log warn "attr_updater failed to get openstack_ports attribute of node $OCF_RESOURCE_INSTANCE" + return $OCF_ERR_GENERIC + fi +@@ -129,7 +129,7 @@ osflip_monitor() { + node_port_ids=$(${HA_SBIN_DIR}/attrd_updater --query -n openstack_ports -N $(crm_node -n) \ + | awk -F= '{gsub("\"","");print $NF}' \ + | tr ',' ' ' \ +- | awk -F: '{print $NF}') ++ | awk '{gsub("[^ ]*:", "");print}') + + # Is the IP active and attached? + result=$($OCF_RESKEY_openstackcli floating ip show \ +diff --git a/heartbeat/openstack-virtual-ip b/heartbeat/openstack-virtual-ip +index a1084c420..c654d980a 100755 +--- a/heartbeat/openstack-virtual-ip ++++ b/heartbeat/openstack-virtual-ip +@@ -119,7 +119,7 @@ osvip_validate() { + get_config + + ${HA_SBIN_DIR}/attrd_updater --query -n openstack_ports -N $(crm_node -n) > /dev/null 2>&1 +- if [ $? -ne 0 ] ; then ++ if [ $? -ne 0 ] && ! ocf_is_probe; then + ocf_log warn "attr_updater failed to get openstack_ports attribute of node $OCF_RESOURCE_INSTANCE" + return $OCF_ERR_GENERIC + fi +@@ -136,7 +136,7 @@ osvip_monitor() { + --format value \ + --column allowed_address_pairs \ + ${node_port_id}) +- if echo $result | grep -q $OCF_RESKEY_ip ; then ++ if echo $result | grep -q "$OCF_RESKEY_ip"; then + ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -S status -n openstack_virtual_ip -v $OCF_RESKEY_ip + + return $OCF_SUCCESS diff --git a/bz1908148-openstack-info-fix-bashism.patch b/bz1908148-openstack-info-fix-bashism.patch new file mode 100644 index 0000000..4f78d54 --- /dev/null +++ b/bz1908148-openstack-info-fix-bashism.patch @@ -0,0 +1,26 @@ +From 8b1d3257e5176a2f50a843a21888c4b4f51f370b Mon Sep 17 00:00:00 2001 +From: Valentin Vidic +Date: Sun, 3 Apr 2022 20:31:50 +0200 +Subject: [PATCH] openstack-info: fix bashism + +Also simplify striping of trailing comma. +--- + heartbeat/openstack-info.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/heartbeat/openstack-info.in b/heartbeat/openstack-info.in +index f6dc1ee4d..f3a59fc7a 100755 +--- a/heartbeat/openstack-info.in ++++ b/heartbeat/openstack-info.in +@@ -167,9 +167,9 @@ OSInfoStats() { + --column fixed_ips \ + ${port_id} | grep -P '\"subnet_id\": \".*\",$' | + grep -P -o '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}') +- value+="${subnet_id}:${port_id}," ++ value="${value}${subnet_id}:${port_id}," + done +- value=$(echo ${value} | sed -e 's/,$//g') ++ value=${value%,} + + ${HA_SBIN_DIR}/attrd_updater ${OCF_RESKEY_delay} -n openstack_ports -v "$value" + diff --git a/bz2049414-Filesystem-1-fix-uuid-label-device-whitespace.patch b/bz2049414-Filesystem-1-fix-uuid-label-device-whitespace.patch new file mode 100644 index 0000000..09960f0 --- /dev/null +++ b/bz2049414-Filesystem-1-fix-uuid-label-device-whitespace.patch @@ -0,0 +1,44 @@ +From 26de0ad2f0f975166fe79ef72ab08e2c03519eea Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Mon, 28 Mar 2022 13:25:35 +0200 +Subject: [PATCH] Filesystem: fix logic for UUID/label devices with space + between parameter and UUID/label + +--- + heartbeat/Filesystem | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/heartbeat/Filesystem b/heartbeat/Filesystem +index 1a90d6a42..72a1b8623 100755 +--- a/heartbeat/Filesystem ++++ b/heartbeat/Filesystem +@@ -596,11 +596,11 @@ Filesystem_start() + flushbufs "$DEVICE" + # Mount the filesystem. + case "$FSTYPE" in +- none) $MOUNT $options "$DEVICE" "$MOUNTPOINT" && ++ none) $MOUNT $options $device_opt "$DEVICE" "$MOUNTPOINT" && + bind_mount + ;; +- "") $MOUNT $options "$DEVICE" "$MOUNTPOINT" ;; +- *) $MOUNT -t "$FSTYPE" $options "$DEVICE" "$MOUNTPOINT" ;; ++ "") $MOUNT $options $device_opt "$DEVICE" "$MOUNTPOINT" ;; ++ *) $MOUNT -t "$FSTYPE" $options $device_opt "$DEVICE" "$MOUNTPOINT" ;; + esac + + if [ $? -ne 0 ]; then +@@ -902,7 +902,13 @@ set_blockdevice_var() { + fi + + case "$DEVICE" in +- -*) # Oh... An option to mount instead... Typically -U or -L ++ --*) # Typically --uuid or --label ++ device_opt=$(echo $DEVICE | sed -E "s/([[:blank:]]|=).*//") ++ DEVICE=$(echo $DEVICE | sed -E "s/$device_opt([[:blank:]]*|=)//") ++ ;; ++ -*) # Oh... An option to mount instead... Typically -U or -L ++ device_opt=$(echo $DEVICE | cut -c1-2) ++ DEVICE=$(echo $DEVICE | sed "s/$device_opt[[:blank:]]*//") + ;; + /dev/null) # Special case for BSC + blockdevice=yes diff --git a/bz2049414-Filesystem-2-improve-uuid-label-device-logic.patch b/bz2049414-Filesystem-2-improve-uuid-label-device-logic.patch new file mode 100644 index 0000000..844772a --- /dev/null +++ b/bz2049414-Filesystem-2-improve-uuid-label-device-logic.patch @@ -0,0 +1,38 @@ +From d9b46474fc19d9c57e2cfb752d60319017da8410 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Wed, 6 Apr 2022 14:14:19 +0200 +Subject: [PATCH] Filesystem: improve logic for UUID/label and add note that + /dev/disk/by-{uuid,label}/ are preferred on Linux + +--- + heartbeat/Filesystem | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/heartbeat/Filesystem b/heartbeat/Filesystem +index 72a1b8623..44270ad98 100755 +--- a/heartbeat/Filesystem ++++ b/heartbeat/Filesystem +@@ -163,6 +163,8 @@ directory where the status file is to be placed. + + + The name of block device for the filesystem, or -U, -L options for mount, or NFS mount specification. ++ ++NOTE: On Linux /dev/disk/by-{uuid,label}/ are preferred to -U/-L. + + block device + +@@ -902,11 +904,11 @@ set_blockdevice_var() { + fi + + case "$DEVICE" in +- --*) # Typically --uuid or --label +- device_opt=$(echo $DEVICE | sed -E "s/([[:blank:]]|=).*//") ++ --uuid=*|--uuid\ *|--label=*|--label\ *) ++ device_opt=$(echo $DEVICE | sed "s/\([[:blank:]]\|=\).*//") + DEVICE=$(echo $DEVICE | sed -E "s/$device_opt([[:blank:]]*|=)//") + ;; +- -*) # Oh... An option to mount instead... Typically -U or -L ++ -U*|-L*) # short versions of --uuid/--label + device_opt=$(echo $DEVICE | cut -c1-2) + DEVICE=$(echo $DEVICE | sed "s/$device_opt[[:blank:]]*//") + ;; diff --git a/bz1654862-1-IPsrcaddr-dhcp-warning.patch b/bz2064342-1-IPsrcaddr-dhcp-warning.patch similarity index 100% rename from bz1654862-1-IPsrcaddr-dhcp-warning.patch rename to bz2064342-1-IPsrcaddr-dhcp-warning.patch diff --git a/bz1654862-2-IPsrcaddr-error-message-route-not-found.patch b/bz2064342-2-IPsrcaddr-error-message-route-not-found.patch similarity index 100% rename from bz1654862-2-IPsrcaddr-error-message-route-not-found.patch rename to bz2064342-2-IPsrcaddr-error-message-route-not-found.patch diff --git a/bz1654862-3-IPsrcaddr-fix-indentation.patch b/bz2064342-3-IPsrcaddr-fix-indentation.patch similarity index 100% rename from bz1654862-3-IPsrcaddr-fix-indentation.patch rename to bz2064342-3-IPsrcaddr-fix-indentation.patch diff --git a/bz1654862-4-IPsrcaddr-fixes.patch b/bz2064342-4-IPsrcaddr-fixes.patch similarity index 100% rename from bz1654862-4-IPsrcaddr-fixes.patch rename to bz2064342-4-IPsrcaddr-fixes.patch diff --git a/bz2072043-LVM-activate-fix-fence-issue.patch b/bz2072043-LVM-activate-fix-fence-issue.patch new file mode 100644 index 0000000..03727c8 --- /dev/null +++ b/bz2072043-LVM-activate-fix-fence-issue.patch @@ -0,0 +1,102 @@ +From e651576c1b5c1ffbe0fd1b78f209be9a3f9764e7 Mon Sep 17 00:00:00 2001 +From: XingWei-Liu +Date: Thu, 10 Mar 2022 10:38:11 +0800 +Subject: [PATCH 1/4] change lvm_status return value from ocf_not_running to + ocf_err_generic + +--- + heartbeat/LVM-activate | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/heartbeat/LVM-activate b/heartbeat/LVM-activate +index aed672ea3..0aef76706 100755 +--- a/heartbeat/LVM-activate ++++ b/heartbeat/LVM-activate +@@ -790,7 +790,7 @@ lvm_status() { + fi + + if [ $dm_count -eq 0 ]; then +- return $OCF_NOT_RUNNING ++ return $OCF_ERR_GENERIC + fi + + case "$OCF_CHECK_LEVEL" in + +From 540ae56436a4f9547bb17aa206fe0e8c7a7fea87 Mon Sep 17 00:00:00 2001 +From: XingWei-Liu +Date: Thu, 10 Mar 2022 16:44:25 +0800 +Subject: [PATCH 2/4] add if ocf_is_probe in monitor func + +--- + heartbeat/LVM-activate | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/heartbeat/LVM-activate b/heartbeat/LVM-activate +index 0aef76706..c86606637 100755 +--- a/heartbeat/LVM-activate ++++ b/heartbeat/LVM-activate +@@ -790,7 +790,11 @@ lvm_status() { + fi + + if [ $dm_count -eq 0 ]; then +- return $OCF_ERR_GENERIC ++ if ocf_is_probe ;then ++ return $OCF_NOT_RUNNING ++ else ++ return $OCF_ERR_GENERIC ++ fi + fi + + case "$OCF_CHECK_LEVEL" in + +From ae3f35d4f671f3288034a257c6dd8eff9a83447a Mon Sep 17 00:00:00 2001 +From: XingWei-Liu +Date: Thu, 10 Mar 2022 16:50:04 +0800 +Subject: [PATCH 3/4] add if ocf_is_probe in monitor func + +--- + heartbeat/LVM-activate | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/heartbeat/LVM-activate b/heartbeat/LVM-activate +index c86606637..f345f73a9 100755 +--- a/heartbeat/LVM-activate ++++ b/heartbeat/LVM-activate +@@ -791,9 +791,9 @@ lvm_status() { + + if [ $dm_count -eq 0 ]; then + if ocf_is_probe ;then +- return $OCF_NOT_RUNNING +- else + return $OCF_ERR_GENERIC ++ else ++ return $OCF_NOT_RUNNING + fi + fi + + +From 1072c0490ef936a1a7dfd8411da434dce1569457 Mon Sep 17 00:00:00 2001 +From: XingWei-Liu +Date: Thu, 10 Mar 2022 18:10:21 +0800 +Subject: [PATCH 4/4] reverse return value in monitor func + +--- + heartbeat/LVM-activate | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/heartbeat/LVM-activate b/heartbeat/LVM-activate +index f345f73a9..c86606637 100755 +--- a/heartbeat/LVM-activate ++++ b/heartbeat/LVM-activate +@@ -791,9 +791,9 @@ lvm_status() { + + if [ $dm_count -eq 0 ]; then + if ocf_is_probe ;then +- return $OCF_ERR_GENERIC +- else + return $OCF_NOT_RUNNING ++ else ++ return $OCF_ERR_GENERIC + fi + fi + diff --git a/bz2086889-lvmlockd-fail-when-use_lvmlockd-not-set.patch b/bz2086889-lvmlockd-fail-when-use_lvmlockd-not-set.patch new file mode 100644 index 0000000..8400437 --- /dev/null +++ b/bz2086889-lvmlockd-fail-when-use_lvmlockd-not-set.patch @@ -0,0 +1,25 @@ +From b3885f7d95fe390371f806c7f3debb3ec8ad012d Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Tue, 7 Jun 2022 15:20:11 +0200 +Subject: [PATCH] lvmlockd: fail when use_lvmlockd has not been set + +--- + heartbeat/lvmlockd | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/heartbeat/lvmlockd b/heartbeat/lvmlockd +index 05bb0a2e5..dc7bd2d7e 100755 +--- a/heartbeat/lvmlockd ++++ b/heartbeat/lvmlockd +@@ -179,6 +179,11 @@ setup_lvm_config() + out=$(lvmconfig 'global/locking_type' 2> /dev/null) + lock_type=$(echo "$out" | cut -d'=' -f2) + ++ if [ -z "$use_lvmlockd" ]; then ++ ocf_exit_reason "\"use_lvmlockd\" not set in /etc/lvm/lvm.conf ..." ++ exit $OCF_ERR_CONFIGURED ++ fi ++ + if [ -n "$use_lvmlockd" ] && [ "$use_lvmlockd" != 1 ] ; then + ocf_log info "setting \"use_lvmlockd=1\" in /etc/lvm/lvm.conf ..." + sed -i 's,^[[:blank:]]*use_lvmlockd[[:blank:]]*=.*,\ \ \ \ use_lvmlockd = 1,g' /etc/lvm/lvm.conf diff --git a/bz2092262-CTDB-move-process-to-root-cgroup-if-rt-enabled.patch b/bz2090370-CTDB-move-process-to-root-cgroup-if-rt-enabled.patch similarity index 100% rename from bz2092262-CTDB-move-process-to-root-cgroup-if-rt-enabled.patch rename to bz2090370-CTDB-move-process-to-root-cgroup-if-rt-enabled.patch diff --git a/bz2093214-aws-vpc-move-ip-add-interface-label-support.patch b/bz2093214-aws-vpc-move-ip-add-interface-label-support.patch new file mode 100644 index 0000000..d1a611c --- /dev/null +++ b/bz2093214-aws-vpc-move-ip-add-interface-label-support.patch @@ -0,0 +1,82 @@ +From 4420ef84f3172c67fc7b8b6ae41ea173de017bf4 Mon Sep 17 00:00:00 2001 +From: Petr Pavlu +Date: Wed, 25 May 2022 15:12:33 +0200 +Subject: [PATCH] aws-vpc-move-ip: Allow to set the interface label + +Add a parameter to specify an interface label to distinguish the IP +address managed by aws-vpc-move-ip, similarly as can be done with +IPaddr2. This allows to easily recognize the address from other +addresses assigned to a given interface. +--- + heartbeat/aws-vpc-move-ip | 30 +++++++++++++++++++++++++++++- + 1 file changed, 29 insertions(+), 1 deletion(-) + +diff --git a/heartbeat/aws-vpc-move-ip b/heartbeat/aws-vpc-move-ip +index 5d5204080..dee040300 100755 +--- a/heartbeat/aws-vpc-move-ip ++++ b/heartbeat/aws-vpc-move-ip +@@ -43,6 +43,7 @@ OCF_RESKEY_address_default="" + OCF_RESKEY_routing_table_default="" + OCF_RESKEY_routing_table_role_default="" + OCF_RESKEY_interface_default="eth0" ++OCF_RESKEY_iflabel_default="" + OCF_RESKEY_monapi_default="false" + OCF_RESKEY_lookup_type_default="InstanceId" + +@@ -54,6 +55,7 @@ OCF_RESKEY_lookup_type_default="InstanceId" + : ${OCF_RESKEY_routing_table=${OCF_RESKEY_routing_table_default}} + : ${OCF_RESKEY_routing_table_role=${OCF_RESKEY_routing_table_role_default}} + : ${OCF_RESKEY_interface=${OCF_RESKEY_interface_default}} ++: ${OCF_RESKEY_iflabel=${OCF_RESKEY_iflabel_default}} + : ${OCF_RESKEY_monapi=${OCF_RESKEY_monapi_default}} + : ${OCF_RESKEY_lookup_type=${OCF_RESKEY_lookup_type_default}} + +@@ -149,6 +151,18 @@ Name of the network interface, i.e. eth0 + + + ++ ++ ++You can specify an additional label for your IP address here. ++This label is appended to your interface name. ++ ++The kernel allows alphanumeric labels up to a maximum length of 15 ++characters including the interface name and colon (e.g. eth0:foobar1234) ++ ++Interface label ++ ++ ++ + + + Enable enhanced monitoring using AWS API calls to check route table entry +@@ -215,6 +229,14 @@ ec2ip_validate() { + return $OCF_ERR_CONFIGURED + fi + ++ if [ -n "$OCF_RESKEY_iflabel" ]; then ++ label=${OCF_RESKEY_interface}:${OFC_RESKEY_iflabel} ++ if [ ${#label} -gt 15 ]; then ++ ocf_exit_reason "Interface label [$label] exceeds maximum character limit of 15" ++ exit $OCF_ERR_CONFIGURED ++ fi ++ fi ++ + TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") + EC2_INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id -H "X-aws-ec2-metadata-token: $TOKEN") + +@@ -363,7 +385,13 @@ ec2ip_get_and_configure() { + + # Reconfigure the local ip address + ec2ip_drop +- cmd="ip addr add ${OCF_RESKEY_ip}/32 dev $OCF_RESKEY_interface" ++ ++ extra_opts="" ++ if [ -n "$OCF_RESKEY_iflabel" ]; then ++ extra_opts="$extra_opts label $OCF_RESKEY_interface:$OCF_RESKEY_iflabel" ++ fi ++ ++ cmd="ip addr add ${OCF_RESKEY_ip}/32 dev $OCF_RESKEY_interface $extra_opts" + ocf_log debug "executing command: $cmd" + $cmd + rc=$? diff --git a/bz2103370-ocf-tester-1-update.patch b/bz2103370-ocf-tester-1-update.patch new file mode 100644 index 0000000..0e32ed2 --- /dev/null +++ b/bz2103370-ocf-tester-1-update.patch @@ -0,0 +1,39 @@ +From 46e8d346ca4803245f51a157591c4df1126d3b49 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Tue, 12 Jul 2022 12:45:52 +0200 +Subject: [PATCH] ocf-tester: use promotable terms + +--- + tools/ocf-tester.in | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/tools/ocf-tester.in b/tools/ocf-tester.in +index 10822a5a06..f1703ba1b7 100755 +--- a/tools/ocf-tester.in ++++ b/tools/ocf-tester.in +@@ -295,10 +295,10 @@ if [ $rc -eq 3 ]; then + + elif [ $rc -eq 8 ]; then + test_command demote "Cleanup, demote" +- assert $? 0 "Your agent was a master and could not be demoted" 1 ++ assert $? 0 "Your agent was promoted and could not be demoted" 1 + + test_command stop "Cleanup, stop" +- assert $? 0 "Your agent was a master and could not be stopped" 1 ++ assert $? 0 "Your agent was promoted and could not be stopped" 1 + + elif [ $rc -ne 7 ]; then + test_command stop +@@ -370,10 +370,10 @@ if [ $has_promote -eq 1 -a $has_demote -eq 1 ]; then + assert $? 0 "Demote failed" 1 + + elif [ $has_promote -eq 0 -a $has_demote -eq 0 ]; then +- info "* Your agent does not support master/slave (optional)" ++ info "* Your agent does not support promotable clones (optional)" + + else +- echo "* Your agent partially supports master/slave" ++ echo "* Your agent partially supports promotable clones" + num_errors=`expr $num_errors + 1` + fi + diff --git a/bz2103370-ocf-tester-2-remove-deprecated-lrmd-lrmadmin-code.patch b/bz2103370-ocf-tester-2-remove-deprecated-lrmd-lrmadmin-code.patch new file mode 100644 index 0000000..a932397 --- /dev/null +++ b/bz2103370-ocf-tester-2-remove-deprecated-lrmd-lrmadmin-code.patch @@ -0,0 +1,166 @@ +From 687aa646852d5fd5d4e811b2ec562ebffa15e23d Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Thu, 14 Jul 2022 14:52:07 +0200 +Subject: [PATCH] ocf-tester: remove deprecated lrmd/lrmadmin code that hasnt + worked since pre-pacemaker days + +--- + tools/ocf-tester.8 | 12 ++----- + tools/ocf-tester.in | 81 --------------------------------------------- + 2 files changed, 2 insertions(+), 91 deletions(-) + +diff --git a/tools/ocf-tester.8 b/tools/ocf-tester.8 +index 850ec0be04..3f398282d2 100644 +--- a/tools/ocf-tester.8 ++++ b/tools/ocf-tester.8 +@@ -1,9 +1,9 @@ +-.TH OCF-TESTER "8" "January 2012" "Tool for testing if a cluster resource is OCF compliant" "System Administration Utilities" ++.TH OCF-TESTER "8" "July 2022" "Tool for testing if a cluster resource is OCF compliant" "System Administration Utilities" + .SH NAME + ocf-tester \- Part of the Linux-HA project + .SH SYNOPSIS + .B ocf-tester +-[\fI-LhvqdX\fR] \fI-n resource_name \fR[\fI-o name=value\fR]\fI* /full/path/to/resource/agent\fR ++[\fI-hvqdX\fR] \fI-n resource_name \fR[\fI-o name=value\fR]\fI* /full/path/to/resource/agent\fR + .SH DESCRIPTION + Tool for testing if a cluster resource is OCF compliant + .SH OPTIONS +@@ -26,11 +26,6 @@ Name of the resource + \fB\-o\fR name=value + Name and value of any parameters required by the agent + .TP +-\fB\-L\fR +-Use lrmadmin/lrmd for tests +-.PP +-Usage: ocf\-tester [\-Lh] \fB\-n\fR resource_name [\-o name=value]* /full/path/to/resource/agent +-.TP + \fB\-h\fR + This text + .TP +@@ -51,6 +46,3 @@ Name of the resource + .TP + \fB\-o\fR name=value + Name and value of any parameters required by the agent +-.TP +-\fB\-L\fR +-Use lrmadmin/lrmd for tests +diff --git a/tools/ocf-tester.in b/tools/ocf-tester.in +index 10822a5a06..15b14e51ea 100755 +--- a/tools/ocf-tester.in ++++ b/tools/ocf-tester.in +@@ -25,8 +25,6 @@ + # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + # + +-LRMD=@libdir@/heartbeat/lrmd +-LRMADMIN=@sbindir@/lrmadmin + DATADIR=@datadir@ + METADATA_LINT="xmllint --noout --valid -" + +@@ -61,7 +59,6 @@ usage() { + echo " -X Turn on RA tracing (expect large output)" + echo " -n name Name of the resource" + echo " -o name=value Name and value of any parameters required by the agent" +- echo " -L Use lrmadmin/lrmd for tests" + exit $1 + } + +@@ -104,7 +101,6 @@ while test "$done" = "0"; do + -o) name=${2%%=*}; value=${2#*=}; + lrm_ra_args="$lrm_ra_args $2"; + ra_args="$ra_args OCF_RESKEY_$name='$value'"; shift; shift;; +- -L) use_lrmd=1; shift;; + -v) verbose=1; shift;; + -d) export HA_debug=1; shift;; + -X) export OCF_TRACE_RA=1; verbose=1; shift;; +@@ -140,79 +136,6 @@ stopped_rc=7 + has_demote=1 + has_promote=1 + +-start_lrmd() { +- lrmd_timeout=0 +- lrmd_interval=0 +- lrmd_target_rc=EVERYTIME +- lrmd_started="" +- $LRMD -s 2>/dev/null +- rc=$? +- if [ $rc -eq 3 ]; then +- lrmd_started=1 +- $LRMD & +- sleep 1 +- $LRMD -s 2>/dev/null +- else +- return $rc +- fi +-} +-add_resource() { +- $LRMADMIN -A $OCF_RESOURCE_INSTANCE \ +- ocf \ +- `basename $agent` \ +- $(basename `dirname $agent`) \ +- $lrm_ra_args > /dev/null +-} +-del_resource() { +- $LRMADMIN -D $OCF_RESOURCE_INSTANCE +-} +-parse_lrmadmin_output() { +- awk ' +-BEGIN{ rc=1; } +-/Waiting for lrmd to callback.../ { n=1; next; } +-n==1 && /----------------operation--------------/ { n++; next; } +-n==2 && /return code:/ { rc=$0; sub("return code: *","",rc); next } +-n==2 && /---------------------------------------/ { +- n++; +- next; +-} +-END{ +- if( n!=3 ) exit 1; +- else exit rc; +-} +-' +-} +-exec_resource() { +- op="$1" +- args="$2" +- $LRMADMIN -E $OCF_RESOURCE_INSTANCE \ +- $op $lrmd_timeout $lrmd_interval \ +- $lrmd_target_rc \ +- $args | parse_lrmadmin_output +-} +- +-if [ "$use_lrmd" = 1 ]; then +- echo "Using lrmd/lrmadmin for all tests" +- start_lrmd || { +- echo "could not start lrmd" >&2 +- exit 1 +- } +- trap ' +- [ "$lrmd_started" = 1 ] && $LRMD -k +- ' EXIT +- add_resource || { +- echo "failed to add resource to lrmd" >&2 +- exit 1 +- } +-fi +- +-lrm_test_command() { +- action="$1" +- msg="$2" +- debug "$msg" +- exec_resource $action "$lrm_ra_args" +-} +- + test_permissions() { + action=meta-data + debug ${1:-"Testing permissions with uid nobody"} +@@ -233,10 +156,6 @@ test_command() { + action=$1; shift + export __OCF_ACTION=$action + msg=${1:-"Testing: $action"} +- if [ "$use_lrmd" = 1 ]; then +- lrm_test_command $action "$msg" +- return $? +- fi + #echo Running: "export $ra_args; $agent $action 2>&1 > /dev/null" + if [ $verbose -eq 0 ]; then + command_output=`$agent $action 2>&1` diff --git a/bz2116941-ethmonitor-ovsmonitor-pgsql-fix-attrd_updater-q.patch b/bz2116941-ethmonitor-ovsmonitor-pgsql-fix-attrd_updater-q.patch new file mode 100644 index 0000000..f4c46f8 --- /dev/null +++ b/bz2116941-ethmonitor-ovsmonitor-pgsql-fix-attrd_updater-q.patch @@ -0,0 +1,75 @@ +From 0063164d72bbaca68f12a2f0a7dbae9ccb41fa39 Mon Sep 17 00:00:00 2001 +From: Oyvind Albrigtsen +Date: Tue, 26 Jul 2022 09:08:26 +0200 +Subject: [PATCH] ethmonitor/ovsmonitor/pgsql: remove ignored attrd_updater + "-q" parameter + +attrd_updater in 2.1.3 no longer ignores the -q parameter, which makes +these agents break. It never did anything in attrd_updater, and is +probably left-over from copy/paste crm_attribute code that got changed +to attrd_updater. +--- + heartbeat/ethmonitor | 2 +- + heartbeat/ovsmonitor | 2 +- + heartbeat/pgsql | 8 ++++---- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/heartbeat/ethmonitor b/heartbeat/ethmonitor +index ba8574131..451738a0b 100755 +--- a/heartbeat/ethmonitor ++++ b/heartbeat/ethmonitor +@@ -464,7 +464,7 @@ END + + set_cib_value() { + local score=`expr $1 \* $OCF_RESKEY_multiplier` +- attrd_updater -n $ATTRNAME -v $score -q ++ attrd_updater -n $ATTRNAME -v $score + local rc=$? + case $rc in + 0) ocf_log debug "attrd_updater: Updated $ATTRNAME = $score" ;; +diff --git a/heartbeat/ovsmonitor b/heartbeat/ovsmonitor +index 872ce86eb..6765da4b9 100755 +--- a/heartbeat/ovsmonitor ++++ b/heartbeat/ovsmonitor +@@ -355,7 +355,7 @@ END + + set_cib_value() { + local score=`expr $1 \* $OCF_RESKEY_multiplier` +- attrd_updater -n $ATTRNAME -v $score -q ++ attrd_updater -n $ATTRNAME -v $score + local rc=$? + case $rc in + 0) ocf_log debug "attrd_updater: Updated $ATTRNAME = $score" ;; +diff --git a/heartbeat/pgsql b/heartbeat/pgsql +index 94aceb324..e93d66855 100755 +--- a/heartbeat/pgsql ++++ b/heartbeat/pgsql +@@ -808,7 +808,7 @@ pgsql_real_stop() { + local stop_escalate + + if ocf_is_true ${OCF_RESKEY_check_wal_receiver}; then +- attrd_updater -n "$PGSQL_WAL_RECEIVER_STATUS_ATTR" -D -q ++ attrd_updater -n "$PGSQL_WAL_RECEIVER_STATUS_ATTR" -D + fi + + if ! pgsql_status +@@ -937,16 +937,16 @@ pgsql_wal_receiver_status() { + receiver_parent_pids=`ps -ef | tr -s " " | grep "[w]al\s*receiver" | cut -d " " -f 3` + + if echo "$receiver_parent_pids" | grep -q -w "$PID" ; then +- attrd_updater -n "$PGSQL_WAL_RECEIVER_STATUS_ATTR" -v "normal" -q ++ attrd_updater -n "$PGSQL_WAL_RECEIVER_STATUS_ATTR" -v "normal" + return 0 + fi + + if [ $pgsql_real_monitor_status -eq "$OCF_RUNNING_MASTER" ]; then +- attrd_updater -n "$PGSQL_WAL_RECEIVER_STATUS_ATTR" -v "normal (master)" -q ++ attrd_updater -n "$PGSQL_WAL_RECEIVER_STATUS_ATTR" -v "normal (master)" + return 0 + fi + +- attrd_updater -n "$PGSQL_WAL_RECEIVER_STATUS_ATTR" -v "ERROR" -q ++ attrd_updater -n "$PGSQL_WAL_RECEIVER_STATUS_ATTR" -v "ERROR" + ocf_log warn "wal receiver process is not running" + return 1 + } diff --git a/bz2130986-azure-events-az-new-ra.patch b/bz2130986-azure-events-az-new-ra.patch new file mode 100644 index 0000000..88c7781 --- /dev/null +++ b/bz2130986-azure-events-az-new-ra.patch @@ -0,0 +1,903 @@ +From 5dcd5153f0318e4766f7f4d3e61dfdb4b352c39c Mon Sep 17 00:00:00 2001 +From: MSSedusch +Date: Mon, 30 May 2022 15:08:10 +0200 +Subject: [PATCH 1/2] add new Azure Events AZ resource agent + +--- + .gitignore | 1 + + configure.ac | 8 + + doc/man/Makefile.am | 4 + + heartbeat/Makefile.am | 4 + + heartbeat/azure-events-az.in | 782 +++++++++++++++++++++++++++++++++++ + 5 files changed, 799 insertions(+) + create mode 100644 heartbeat/azure-events-az.in + +diff --git a/.gitignore b/.gitignore +index 0c259b5cf..e2b7c039c 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -54,6 +54,7 @@ heartbeat/Squid + heartbeat/SysInfo + heartbeat/aws-vpc-route53 + heartbeat/azure-events ++heartbeat/azure-events-az + heartbeat/clvm + heartbeat/conntrackd + heartbeat/dnsupdate +diff --git a/configure.ac b/configure.ac +index eeecfad0e..5716a2be2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -523,6 +523,13 @@ if test -z "$PYTHON" || test $BUILD_OCF_PY -eq 0; then + fi + AM_CONDITIONAL(BUILD_AZURE_EVENTS, test $BUILD_AZURE_EVENTS -eq 1) + ++BUILD_AZURE_EVENTS_AZ=1 ++if test -z "$PYTHON" || test $BUILD_OCF_PY -eq 0; then ++ BUILD_AZURE_EVENTS_AZ=0 ++ AC_MSG_WARN("Not building azure-events-az") ++fi ++AM_CONDITIONAL(BUILD_AZURE_EVENTS_AZ, test $BUILD_AZURE_EVENTS_AZ -eq 1) ++ + BUILD_GCP_PD_MOVE=1 + if test -z "$PYTHON" || test "x${HAVE_PYMOD_GOOGLEAPICLIENT}" != xyes || test $BUILD_OCF_PY -eq 0; then + BUILD_GCP_PD_MOVE=0 +@@ -976,6 +983,7 @@ rgmanager/Makefile \ + + dnl Files we output that need to be executable + AC_CONFIG_FILES([heartbeat/azure-events], [chmod +x heartbeat/azure-events]) ++AC_CONFIG_FILES([heartbeat/azure-events-az], [chmod +x heartbeat/azure-events-az]) + AC_CONFIG_FILES([heartbeat/AoEtarget], [chmod +x heartbeat/AoEtarget]) + AC_CONFIG_FILES([heartbeat/ManageRAID], [chmod +x heartbeat/ManageRAID]) + AC_CONFIG_FILES([heartbeat/ManageVE], [chmod +x heartbeat/ManageVE]) +diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am +index cd8fd16bf..658c700ac 100644 +--- a/doc/man/Makefile.am ++++ b/doc/man/Makefile.am +@@ -219,6 +219,10 @@ if BUILD_AZURE_EVENTS + man_MANS += ocf_heartbeat_azure-events.7 + endif + ++if BUILD_AZURE_EVENTS_AZ ++man_MANS += ocf_heartbeat_azure-events-az.7 ++endif ++ + if BUILD_GCP_PD_MOVE + man_MANS += ocf_heartbeat_gcp-pd-move.7 + endif +diff --git a/heartbeat/Makefile.am b/heartbeat/Makefile.am +index 20d41e36a..1133dc13e 100644 +--- a/heartbeat/Makefile.am ++++ b/heartbeat/Makefile.am +@@ -188,6 +188,10 @@ if BUILD_AZURE_EVENTS + ocf_SCRIPTS += azure-events + endif + ++if BUILD_AZURE_EVENTS_AZ ++ocf_SCRIPTS += azure-events-az ++endif ++ + if BUILD_GCP_PD_MOVE + ocf_SCRIPTS += gcp-pd-move + endif +diff --git a/heartbeat/azure-events-az.in b/heartbeat/azure-events-az.in +new file mode 100644 +index 000000000..616fc8d9e +--- /dev/null ++++ b/heartbeat/azure-events-az.in +@@ -0,0 +1,782 @@ ++#!@PYTHON@ -tt ++# ++# Resource agent for monitoring Azure Scheduled Events ++# ++# License: GNU General Public License (GPL) ++# (c) 2018 Tobias Niekamp, Microsoft Corp. ++# and Linux-HA contributors ++ ++import os ++import sys ++import time ++import subprocess ++import json ++try: ++ import urllib2 ++ from urllib2 import URLError ++except ImportError: ++ import urllib.request as urllib2 ++ from urllib.error import URLError ++import socket ++from collections import defaultdict ++ ++OCF_FUNCTIONS_DIR = os.environ.get("OCF_FUNCTIONS_DIR", "%s/lib/heartbeat" % os.environ.get("OCF_ROOT")) ++sys.path.append(OCF_FUNCTIONS_DIR) ++import ocf ++ ++############################################################################## ++ ++ ++VERSION = "0.10" ++USER_AGENT = "Pacemaker-ResourceAgent/%s %s" % (VERSION, ocf.distro()) ++ ++attr_globalPullState = "azure-events-az_globalPullState" ++attr_lastDocVersion = "azure-events-az_lastDocVersion" ++attr_curNodeState = "azure-events-az_curNodeState" ++attr_pendingEventIDs = "azure-events-az_pendingEventIDs" ++attr_healthstate = "#health-azure" ++ ++default_loglevel = ocf.logging.INFO ++default_relevantEventTypes = set(["Reboot", "Redeploy"]) ++ ++global_pullMaxAttempts = 3 ++global_pullDelaySecs = 1 ++ ++############################################################################## ++ ++class attrDict(defaultdict): ++ """ ++ A wrapper for accessing dict keys like an attribute ++ """ ++ def __init__(self, data): ++ super(attrDict, self).__init__(attrDict) ++ for d in data.keys(): ++ self.__setattr__(d, data[d]) ++ ++ def __getattr__(self, key): ++ try: ++ return self[key] ++ except KeyError: ++ raise AttributeError(key) ++ ++ def __setattr__(self, key, value): ++ self[key] = value ++ ++############################################################################## ++ ++class azHelper: ++ """ ++ Helper class for Azure's metadata API (including Scheduled Events) ++ """ ++ metadata_host = "http://169.254.169.254/metadata" ++ instance_api = "instance" ++ events_api = "scheduledevents" ++ api_version = "2019-08-01" ++ ++ @staticmethod ++ def _sendMetadataRequest(endpoint, postData=None): ++ """ ++ Send a request to Azure's Azure Metadata Service API ++ """ ++ url = "%s/%s?api-version=%s" % (azHelper.metadata_host, endpoint, azHelper.api_version) ++ data = "" ++ ocf.logger.debug("_sendMetadataRequest: begin; endpoint = %s, postData = %s" % (endpoint, postData)) ++ ocf.logger.debug("_sendMetadataRequest: url = %s" % url) ++ ++ if postData and type(postData) != bytes: ++ postData = postData.encode() ++ ++ req = urllib2.Request(url, postData) ++ req.add_header("Metadata", "true") ++ req.add_header("User-Agent", USER_AGENT) ++ try: ++ resp = urllib2.urlopen(req) ++ except URLError as e: ++ if hasattr(e, 'reason'): ++ ocf.logger.warning("Failed to reach the server: %s" % e.reason) ++ clusterHelper.setAttr(attr_globalPullState, "IDLE") ++ elif hasattr(e, 'code'): ++ ocf.logger.warning("The server couldn\'t fulfill the request. Error code: %s" % e.code) ++ clusterHelper.setAttr(attr_globalPullState, "IDLE") ++ else: ++ data = resp.read() ++ ocf.logger.debug("_sendMetadataRequest: response = %s" % data) ++ ++ if data: ++ data = json.loads(data) ++ ++ ocf.logger.debug("_sendMetadataRequest: finished") ++ return data ++ ++ @staticmethod ++ def getInstanceInfo(): ++ """ ++ Fetch details about the current VM from Azure's Azure Metadata Service API ++ """ ++ ocf.logger.debug("getInstanceInfo: begin") ++ ++ jsondata = azHelper._sendMetadataRequest(azHelper.instance_api) ++ ocf.logger.debug("getInstanceInfo: json = %s" % jsondata) ++ ++ if jsondata: ++ ocf.logger.debug("getInstanceInfo: finished, returning {}".format(jsondata["compute"])) ++ return attrDict(jsondata["compute"]) ++ else: ++ ocf.ocf_exit_reason("getInstanceInfo: Unable to get instance info") ++ sys.exit(ocf.OCF_ERR_GENERIC) ++ ++ @staticmethod ++ def pullScheduledEvents(): ++ """ ++ Retrieve all currently scheduled events via Azure Metadata Service API ++ """ ++ ocf.logger.debug("pullScheduledEvents: begin") ++ ++ jsondata = azHelper._sendMetadataRequest(azHelper.events_api) ++ ocf.logger.debug("pullScheduledEvents: json = %s" % jsondata) ++ ++ ocf.logger.debug("pullScheduledEvents: finished") ++ return attrDict(jsondata) ++ ++ @staticmethod ++ def forceEvents(eventIDs): ++ """ ++ Force a set of events to start immediately ++ """ ++ ocf.logger.debug("forceEvents: begin") ++ ++ events = [] ++ for e in eventIDs: ++ events.append({ ++ "EventId": e, ++ }) ++ postData = { ++ "StartRequests" : events ++ } ++ ocf.logger.info("forceEvents: postData = %s" % postData) ++ resp = azHelper._sendMetadataRequest(azHelper.events_api, postData=json.dumps(postData)) ++ ++ ocf.logger.debug("forceEvents: finished") ++ return ++ ++############################################################################## ++ ++class clusterHelper: ++ """ ++ Helper functions for Pacemaker control via crm ++ """ ++ @staticmethod ++ def _getLocation(node): ++ """ ++ Helper function to retrieve local/global attributes ++ """ ++ if node: ++ return ["--node", node] ++ else: ++ return ["--type", "crm_config"] ++ ++ @staticmethod ++ def _exec(command, *args): ++ """ ++ Helper function to execute a UNIX command ++ """ ++ args = list(args) ++ ocf.logger.debug("_exec: begin; command = %s, args = %s" % (command, str(args))) ++ ++ def flatten(*n): ++ return (str(e) for a in n ++ for e in (flatten(*a) if isinstance(a, (tuple, list)) else (str(a),))) ++ command = list(flatten([command] + args)) ++ ocf.logger.debug("_exec: cmd = %s" % " ".join(command)) ++ try: ++ ret = subprocess.check_output(command) ++ if type(ret) != str: ++ ret = ret.decode() ++ ocf.logger.debug("_exec: return = %s" % ret) ++ return ret.rstrip() ++ except Exception as err: ++ ocf.logger.exception(err) ++ return None ++ ++ @staticmethod ++ def setAttr(key, value, node=None): ++ """ ++ Set the value of a specific global/local attribute in the Pacemaker cluster ++ """ ++ ocf.logger.debug("setAttr: begin; key = %s, value = %s, node = %s" % (key, value, node)) ++ ++ if value: ++ ret = clusterHelper._exec("crm_attribute", ++ "--name", key, ++ "--update", value, ++ clusterHelper._getLocation(node)) ++ else: ++ ret = clusterHelper._exec("crm_attribute", ++ "--name", key, ++ "--delete", ++ clusterHelper._getLocation(node)) ++ ++ ocf.logger.debug("setAttr: finished") ++ return len(ret) == 0 ++ ++ @staticmethod ++ def getAttr(key, node=None): ++ """ ++ Retrieve a global/local attribute from the Pacemaker cluster ++ """ ++ ocf.logger.debug("getAttr: begin; key = %s, node = %s" % (key, node)) ++ ++ val = clusterHelper._exec("crm_attribute", ++ "--name", key, ++ "--query", "--quiet", ++ "--default", "", ++ clusterHelper._getLocation(node)) ++ ocf.logger.debug("getAttr: finished") ++ if not val: ++ return None ++ return val if not val.isdigit() else int(val) ++ ++ @staticmethod ++ def getAllNodes(): ++ """ ++ Get a list of hostnames for all nodes in the Pacemaker cluster ++ """ ++ ocf.logger.debug("getAllNodes: begin") ++ ++ nodes = [] ++ nodeList = clusterHelper._exec("crm_node", "--list") ++ for n in nodeList.split("\n"): ++ nodes.append(n.split()[1]) ++ ocf.logger.debug("getAllNodes: finished; return %s" % str(nodes)) ++ ++ return nodes ++ ++ @staticmethod ++ def getHostNameFromAzName(azName): ++ """ ++ Helper function to get the actual host name from an Azure node name ++ """ ++ return clusterHelper.getAttr("hostName_%s" % azName) ++ ++ @staticmethod ++ def removeHoldFromNodes(): ++ """ ++ Remove the ON_HOLD state from all nodes in the Pacemaker cluster ++ """ ++ ocf.logger.debug("removeHoldFromNodes: begin") ++ ++ for n in clusterHelper.getAllNodes(): ++ if clusterHelper.getAttr(attr_curNodeState, node=n) == "ON_HOLD": ++ clusterHelper.setAttr(attr_curNodeState, "AVAILABLE", node=n) ++ ocf.logger.info("removeHoldFromNodes: removed ON_HOLD from node %s" % n) ++ ++ ocf.logger.debug("removeHoldFromNodes: finished") ++ return False ++ ++ @staticmethod ++ def otherNodesAvailable(exceptNode): ++ """ ++ Check if there are any nodes (except a given node) in the Pacemaker cluster that have state AVAILABLE ++ """ ++ ocf.logger.debug("otherNodesAvailable: begin; exceptNode = %s" % exceptNode) ++ ++ for n in clusterHelper.getAllNodes(): ++ state = clusterHelper.getAttr(attr_curNodeState, node=n) ++ state = stringToNodeState(state) if state else AVAILABLE ++ if state == AVAILABLE and n != exceptNode.hostName: ++ ocf.logger.info("otherNodesAvailable: at least %s is available" % n) ++ ocf.logger.debug("otherNodesAvailable: finished") ++ return True ++ ocf.logger.info("otherNodesAvailable: no other nodes are available") ++ ocf.logger.debug("otherNodesAvailable: finished") ++ ++ return False ++ ++ @staticmethod ++ def transitionSummary(): ++ """ ++ Get the current Pacemaker transition summary (used to check if all resources are stopped when putting a node standby) ++ """ ++ # Is a global crm_simulate "too much"? Or would it be sufficient it there are no planned transitions for a particular node? ++ # # crm_simulate -Ls ++ # Transition Summary: ++ # * Promote rsc_SAPHana_HN1_HDB03:0 (Slave -> Master hsr3-db1) ++ # * Stop rsc_SAPHana_HN1_HDB03:1 (hsr3-db0) ++ # * Move rsc_ip_HN1_HDB03 (Started hsr3-db0 -> hsr3-db1) ++ # * Start rsc_nc_HN1_HDB03 (hsr3-db1) ++ # # Excepted result when there are no pending actions: ++ # Transition Summary: ++ ocf.logger.debug("transitionSummary: begin") ++ ++ summary = clusterHelper._exec("crm_simulate", "-Ls") ++ if not summary: ++ ocf.logger.warning("transitionSummary: could not load transition summary") ++ return False ++ if summary.find("Transition Summary:") < 0: ++ ocf.logger.warning("transitionSummary: received unexpected transition summary: %s" % summary) ++ return False ++ summary = summary.split("Transition Summary:")[1] ++ ret = summary.split("\n").pop(0) ++ ++ ocf.logger.debug("transitionSummary: finished; return = %s" % str(ret)) ++ return ret ++ ++ @staticmethod ++ def listOperationsOnNode(node): ++ """ ++ Get a list of all current operations for a given node (used to check if any resources are pending) ++ """ ++ # hsr3-db1:/home/tniek # crm_resource --list-operations -N hsr3-db0 ++ # rsc_azure-events-az (ocf::heartbeat:azure-events-az): Started: rsc_azure-events-az_start_0 (node=hsr3-db0, call=91, rc=0, last-rc-change=Fri Jun 8 22:37:46 2018, exec=115ms): complete ++ # rsc_azure-events-az (ocf::heartbeat:azure-events-az): Started: rsc_azure-events-az_monitor_10000 (node=hsr3-db0, call=93, rc=0, last-rc-change=Fri Jun 8 22:37:47 2018, exec=197ms): complete ++ # rsc_SAPHana_HN1_HDB03 (ocf::suse:SAPHana): Master: rsc_SAPHana_HN1_HDB03_start_0 (node=hsr3-db0, call=-1, rc=193, last-rc-change=Fri Jun 8 22:37:46 2018, exec=0ms): pending ++ # rsc_SAPHanaTopology_HN1_HDB03 (ocf::suse:SAPHanaTopology): Started: rsc_SAPHanaTopology_HN1_HDB03_start_0 (node=hsr3-db0, call=90, rc=0, last-rc-change=Fri Jun 8 22:37:46 2018, exec=3214ms): complete ++ ocf.logger.debug("listOperationsOnNode: begin; node = %s" % node) ++ ++ resources = clusterHelper._exec("crm_resource", "--list-operations", "-N", node) ++ if len(resources) == 0: ++ ret = [] ++ else: ++ ret = resources.split("\n") ++ ++ ocf.logger.debug("listOperationsOnNode: finished; return = %s" % str(ret)) ++ return ret ++ ++ @staticmethod ++ def noPendingResourcesOnNode(node): ++ """ ++ Check that there are no pending resources on a given node ++ """ ++ ocf.logger.debug("noPendingResourcesOnNode: begin; node = %s" % node) ++ ++ for r in clusterHelper.listOperationsOnNode(node): ++ ocf.logger.debug("noPendingResourcesOnNode: * %s" % r) ++ resource = r.split()[-1] ++ if resource == "pending": ++ ocf.logger.info("noPendingResourcesOnNode: found resource %s that is still pending" % resource) ++ ocf.logger.debug("noPendingResourcesOnNode: finished; return = False") ++ return False ++ ocf.logger.info("noPendingResourcesOnNode: no pending resources on node %s" % node) ++ ocf.logger.debug("noPendingResourcesOnNode: finished; return = True") ++ ++ return True ++ ++ @staticmethod ++ def allResourcesStoppedOnNode(node): ++ """ ++ Check that all resources on a given node are stopped ++ """ ++ ocf.logger.debug("allResourcesStoppedOnNode: begin; node = %s" % node) ++ ++ if clusterHelper.noPendingResourcesOnNode(node): ++ if len(clusterHelper.transitionSummary()) == 0: ++ ocf.logger.info("allResourcesStoppedOnNode: no pending resources on node %s and empty transition summary" % node) ++ ocf.logger.debug("allResourcesStoppedOnNode: finished; return = True") ++ return True ++ ocf.logger.info("allResourcesStoppedOnNode: transition summary is not empty") ++ ocf.logger.debug("allResourcesStoppedOnNode: finished; return = False") ++ return False ++ ++ ocf.logger.info("allResourcesStoppedOnNode: still pending resources on node %s" % node) ++ ocf.logger.debug("allResourcesStoppedOnNode: finished; return = False") ++ return False ++ ++############################################################################## ++ ++AVAILABLE = 0 # Node is online and ready to handle events ++STOPPING = 1 # Standby has been triggered, but some resources are still running ++IN_EVENT = 2 # All resources are stopped, and event has been initiated via Azure Metadata Service ++ON_HOLD = 3 # Node has a pending event that cannot be started there are no other nodes available ++ ++def stringToNodeState(name): ++ if type(name) == int: return name ++ if name == "STOPPING": return STOPPING ++ if name == "IN_EVENT": return IN_EVENT ++ if name == "ON_HOLD": return ON_HOLD ++ return AVAILABLE ++ ++def nodeStateToString(state): ++ if state == STOPPING: return "STOPPING" ++ if state == IN_EVENT: return "IN_EVENT" ++ if state == ON_HOLD: return "ON_HOLD" ++ return "AVAILABLE" ++ ++############################################################################## ++ ++class Node: ++ """ ++ Core class implementing logic for a cluster node ++ """ ++ def __init__(self, ra): ++ self.raOwner = ra ++ self.azInfo = azHelper.getInstanceInfo() ++ self.azName = self.azInfo.name ++ self.hostName = socket.gethostname() ++ self.setAttr("azName", self.azName) ++ clusterHelper.setAttr("hostName_%s" % self.azName, self.hostName) ++ ++ def getAttr(self, key): ++ """ ++ Get a local attribute ++ """ ++ return clusterHelper.getAttr(key, node=self.hostName) ++ ++ def setAttr(self, key, value): ++ """ ++ Set a local attribute ++ """ ++ return clusterHelper.setAttr(key, value, node=self.hostName) ++ ++ def selfOrOtherNode(self, node): ++ """ ++ Helper function to distinguish self/other node ++ """ ++ return node if node else self.hostName ++ ++ def setState(self, state, node=None): ++ """ ++ Set the state for a given node (or self) ++ """ ++ node = self.selfOrOtherNode(node) ++ ocf.logger.debug("setState: begin; node = %s, state = %s" % (node, nodeStateToString(state))) ++ ++ clusterHelper.setAttr(attr_curNodeState, nodeStateToString(state), node=node) ++ ++ ocf.logger.debug("setState: finished") ++ ++ def getState(self, node=None): ++ """ ++ Get the state for a given node (or self) ++ """ ++ node = self.selfOrOtherNode(node) ++ ocf.logger.debug("getState: begin; node = %s" % node) ++ ++ state = clusterHelper.getAttr(attr_curNodeState, node=node) ++ ocf.logger.debug("getState: state = %s" % state) ++ ocf.logger.debug("getState: finished") ++ if not state: ++ return AVAILABLE ++ return stringToNodeState(state) ++ ++ def setEventIDs(self, eventIDs, node=None): ++ """ ++ Set pending EventIDs for a given node (or self) ++ """ ++ node = self.selfOrOtherNode(node) ++ ocf.logger.debug("setEventIDs: begin; node = %s, eventIDs = %s" % (node, str(eventIDs))) ++ ++ if eventIDs: ++ eventIDStr = ",".join(eventIDs) ++ else: ++ eventIDStr = None ++ clusterHelper.setAttr(attr_pendingEventIDs, eventIDStr, node=node) ++ ++ ocf.logger.debug("setEventIDs: finished") ++ return ++ ++ def getEventIDs(self, node=None): ++ """ ++ Get pending EventIDs for a given node (or self) ++ """ ++ node = self.selfOrOtherNode(node) ++ ocf.logger.debug("getEventIDs: begin; node = %s" % node) ++ ++ eventIDStr = clusterHelper.getAttr(attr_pendingEventIDs, node=node) ++ if eventIDStr: ++ eventIDs = eventIDStr.split(",") ++ else: ++ eventIDs = None ++ ++ ocf.logger.debug("getEventIDs: finished; eventIDs = %s" % str(eventIDs)) ++ return eventIDs ++ ++ def updateNodeStateAndEvents(self, state, eventIDs, node=None): ++ """ ++ Set the state and pending EventIDs for a given node (or self) ++ """ ++ ocf.logger.debug("updateNodeStateAndEvents: begin; node = %s, state = %s, eventIDs = %s" % (node, nodeStateToString(state), str(eventIDs))) ++ ++ self.setState(state, node=node) ++ self.setEventIDs(eventIDs, node=node) ++ ++ ocf.logger.debug("updateNodeStateAndEvents: finished") ++ return state ++ ++ def putNodeStandby(self, node=None): ++ """ ++ Put self to standby ++ """ ++ node = self.selfOrOtherNode(node) ++ ocf.logger.debug("putNodeStandby: begin; node = %s" % node) ++ ++ clusterHelper._exec("crm_attribute", ++ "--node", node, ++ "--name", attr_healthstate, ++ "--update", "-1000000", ++ "--lifetime=forever") ++ ++ ocf.logger.debug("putNodeStandby: finished") ++ ++ def isNodeInStandby(self, node=None): ++ """ ++ check if node is in standby ++ """ ++ node = self.selfOrOtherNode(node) ++ ocf.logger.debug("isNodeInStandby: begin; node = %s" % node) ++ isInStandy = False ++ ++ healthAttributeStr = clusterHelper.getAttr(attr_healthstate, node) ++ if healthAttributeStr is not None: ++ try: ++ healthAttribute = int(healthAttributeStr) ++ isInStandy = healthAttribute < 0 ++ except ValueError: ++ # Handle the exception ++ ocf.logger.warn("Health attribute %s on node %s cannot be converted to an integer value" % (healthAttributeStr, node)) ++ ++ ocf.logger.debug("isNodeInStandby: finished - result %s" % isInStandy) ++ return isInStandy ++ ++ def putNodeOnline(self, node=None): ++ """ ++ Put self back online ++ """ ++ node = self.selfOrOtherNode(node) ++ ocf.logger.debug("putNodeOnline: begin; node = %s" % node) ++ ++ clusterHelper._exec("crm_attribute", ++ "--node", node, ++ "--name", "#health-azure", ++ "--update", "0", ++ "--lifetime=forever") ++ ++ ocf.logger.debug("putNodeOnline: finished") ++ ++ def separateEvents(self, events): ++ """ ++ Split own/other nodes' events ++ """ ++ ocf.logger.debug("separateEvents: begin; events = %s" % str(events)) ++ ++ localEvents = [] ++ remoteEvents = [] ++ for e in events: ++ e = attrDict(e) ++ if e.EventType not in self.raOwner.relevantEventTypes: ++ continue ++ if self.azName in e.Resources: ++ localEvents.append(e) ++ else: ++ remoteEvents.append(e) ++ ocf.logger.debug("separateEvents: finished; localEvents = %s, remoteEvents = %s" % (str(localEvents), str(remoteEvents))) ++ return (localEvents, remoteEvents) ++ ++############################################################################## ++ ++class raAzEvents: ++ """ ++ Main class for resource agent ++ """ ++ def __init__(self, relevantEventTypes): ++ self.node = Node(self) ++ self.relevantEventTypes = relevantEventTypes ++ ++ def monitor(self): ++ ocf.logger.debug("monitor: begin") ++ ++ events = azHelper.pullScheduledEvents() ++ ++ # get current document version ++ curDocVersion = events.DocumentIncarnation ++ lastDocVersion = self.node.getAttr(attr_lastDocVersion) ++ ocf.logger.debug("monitor: lastDocVersion = %s; curDocVersion = %s" % (lastDocVersion, curDocVersion)) ++ ++ # split events local/remote ++ (localEvents, remoteEvents) = self.node.separateEvents(events.Events) ++ ++ # ensure local events are only executing once ++ if curDocVersion == lastDocVersion: ++ ocf.logger.info("monitor: already handled curDocVersion, skip") ++ return ocf.OCF_SUCCESS ++ ++ localAzEventIDs = set() ++ for e in localEvents: ++ localAzEventIDs.add(e.EventId) ++ ++ curState = self.node.getState() ++ clusterEventIDs = self.node.getEventIDs() ++ ++ ocf.logger.debug("monitor: curDocVersion has not been handled yet") ++ ++ if clusterEventIDs: ++ # there are pending events set, so our state must be STOPPING or IN_EVENT ++ i = 0; touchedEventIDs = False ++ while i < len(clusterEventIDs): ++ # clean up pending events that are already finished according to AZ ++ if clusterEventIDs[i] not in localAzEventIDs: ++ ocf.logger.info("monitor: remove finished local clusterEvent %s" % (clusterEventIDs[i])) ++ clusterEventIDs.pop(i) ++ touchedEventIDs = True ++ else: ++ i += 1 ++ if len(clusterEventIDs) > 0: ++ # there are still pending events (either because we're still stopping, or because the event is still in place) ++ # either way, we need to wait ++ if touchedEventIDs: ++ ocf.logger.info("monitor: added new local clusterEvent %s" % str(clusterEventIDs)) ++ self.node.setEventIDs(clusterEventIDs) ++ else: ++ ocf.logger.info("monitor: no local clusterEvents were updated") ++ else: ++ # there are no more pending events left after cleanup ++ if clusterHelper.noPendingResourcesOnNode(self.node.hostName): ++ # and no pending resources on the node -> set it back online ++ ocf.logger.info("monitor: all local events finished -> clean up, put node online and AVAILABLE") ++ curState = self.node.updateNodeStateAndEvents(AVAILABLE, None) ++ self.node.putNodeOnline() ++ clusterHelper.removeHoldFromNodes() ++ # If Azure Scheduled Events are not used for 24 hours (e.g. because the cluster was asleep), it will be disabled for a VM. ++ # When the cluster wakes up and starts using it again, the DocumentIncarnation is reset. ++ # We need to remove it during cleanup, otherwise azure-events-az will not process the event after wakeup ++ self.node.setAttr(attr_lastDocVersion, None) ++ else: ++ ocf.logger.info("monitor: all local events finished, but some resources have not completed startup yet -> wait") ++ else: ++ if curState == AVAILABLE: ++ if len(localAzEventIDs) > 0: ++ if clusterHelper.otherNodesAvailable(self.node): ++ ocf.logger.info("monitor: can handle local events %s -> set state STOPPING" % (str(localAzEventIDs))) ++ curState = self.node.updateNodeStateAndEvents(STOPPING, localAzEventIDs) ++ else: ++ ocf.logger.info("monitor: cannot handle azEvents %s (only node available) -> set state ON_HOLD" % str(localAzEventIDs)) ++ self.node.setState(ON_HOLD) ++ else: ++ ocf.logger.debug("monitor: no local azEvents to handle") ++ ++ if curState == STOPPING: ++ eventIDsForNode = {} ++ if clusterHelper.noPendingResourcesOnNode(self.node.hostName): ++ if not self.node.isNodeInStandby(): ++ ocf.logger.info("monitor: all local resources are started properly -> put node standby and exit") ++ self.node.putNodeStandby() ++ return ocf.OCF_SUCCESS ++ ++ for e in localEvents: ++ ocf.logger.info("monitor: handling remote event %s (%s; nodes = %s)" % (e.EventId, e.EventType, str(e.Resources))) ++ # before we can force an event to start, we need to ensure all nodes involved have stopped their resources ++ if e.EventStatus == "Scheduled": ++ allNodesStopped = True ++ for azName in e.Resources: ++ hostName = clusterHelper.getHostNameFromAzName(azName) ++ state = self.node.getState(node=hostName) ++ if state == STOPPING: ++ # the only way we can continue is when node state is STOPPING, but all resources have been stopped ++ if not clusterHelper.allResourcesStoppedOnNode(hostName): ++ ocf.logger.info("monitor: (at least) node %s has still resources running -> wait" % hostName) ++ allNodesStopped = False ++ break ++ elif state in (AVAILABLE, IN_EVENT, ON_HOLD): ++ ocf.logger.info("monitor: node %s is still %s -> remote event needs to be picked up locally" % (hostName, nodeStateToString(state))) ++ allNodesStopped = False ++ break ++ if allNodesStopped: ++ ocf.logger.info("monitor: nodes %s are stopped -> add remote event %s to force list" % (str(e.Resources), e.EventId)) ++ for n in e.Resources: ++ hostName = clusterHelper.getHostNameFromAzName(n) ++ if hostName in eventIDsForNode: ++ eventIDsForNode[hostName].append(e.EventId) ++ else: ++ eventIDsForNode[hostName] = [e.EventId] ++ elif e.EventStatus == "Started": ++ ocf.logger.info("monitor: remote event already started") ++ ++ # force the start of all events whose nodes are ready (i.e. have no more resources running) ++ if len(eventIDsForNode.keys()) > 0: ++ eventIDsToForce = set([item for sublist in eventIDsForNode.values() for item in sublist]) ++ ocf.logger.info("monitor: set nodes %s to IN_EVENT; force remote events %s" % (str(eventIDsForNode.keys()), str(eventIDsToForce))) ++ for node, eventId in eventIDsForNode.items(): ++ self.node.updateNodeStateAndEvents(IN_EVENT, eventId, node=node) ++ azHelper.forceEvents(eventIDsToForce) ++ self.node.setAttr(attr_lastDocVersion, curDocVersion) ++ else: ++ ocf.logger.info("monitor: some local resources are not clean yet -> wait") ++ ++ ocf.logger.debug("monitor: finished") ++ return ocf.OCF_SUCCESS ++ ++############################################################################## ++ ++def setLoglevel(verbose): ++ # set up writing into syslog ++ loglevel = default_loglevel ++ if verbose: ++ opener = urllib2.build_opener(urllib2.HTTPHandler(debuglevel=1)) ++ urllib2.install_opener(opener) ++ loglevel = ocf.logging.DEBUG ++ ocf.log.setLevel(loglevel) ++ ++description = ( ++ "Microsoft Azure Scheduled Events monitoring agent", ++ """This resource agent implements a monitor for scheduled ++(maintenance) events for a Microsoft Azure VM. ++ ++If any relevant events are found, it moves all Pacemaker resources ++away from the affected node to allow for a graceful shutdown. ++ ++ Usage: ++ [OCF_RESKEY_eventTypes=VAL] [OCF_RESKEY_verbose=VAL] azure-events-az ACTION ++ ++ action (required): Supported values: monitor, help, meta-data ++ eventTypes (optional): List of event types to be considered ++ relevant by the resource agent (comma-separated). ++ Supported values: Freeze,Reboot,Redeploy ++ Default = Reboot,Redeploy ++/ verbose (optional): If set to true, displays debug info. ++ Default = false ++ ++ Deployment: ++ crm configure primitive rsc_azure-events-az ocf:heartbeat:azure-events-az \ ++ op monitor interval=10s ++ crm configure clone cln_azure-events-az rsc_azure-events-az ++ ++For further information on Microsoft Azure Scheduled Events, please ++refer to the following documentation: ++https://docs.microsoft.com/en-us/azure/virtual-machines/linux/scheduled-events ++""") ++ ++def monitor_action(eventTypes): ++ relevantEventTypes = set(eventTypes.split(",") if eventTypes else []) ++ ra = raAzEvents(relevantEventTypes) ++ return ra.monitor() ++ ++def validate_action(eventTypes): ++ if eventTypes: ++ for event in eventTypes.split(","): ++ if event not in ("Freeze", "Reboot", "Redeploy"): ++ ocf.ocf_exit_reason("Event type not one of Freeze, Reboot, Redeploy: " + eventTypes) ++ return ocf.OCF_ERR_CONFIGURED ++ return ocf.OCF_SUCCESS ++ ++def main(): ++ agent = ocf.Agent("azure-events-az", shortdesc=description[0], longdesc=description[1]) ++ agent.add_parameter( ++ "eventTypes", ++ shortdesc="List of resources to be considered", ++ longdesc="A comma-separated list of event types that will be handled by this resource agent. (Possible values: Freeze,Reboot,Redeploy)", ++ content_type="string", ++ default="Reboot,Redeploy") ++ agent.add_parameter( ++ "verbose", ++ shortdesc="Enable verbose agent logging", ++ longdesc="Set to true to enable verbose logging", ++ content_type="boolean", ++ default="false") ++ agent.add_action("start", timeout=10, handler=lambda: ocf.OCF_SUCCESS) ++ agent.add_action("stop", timeout=10, handler=lambda: ocf.OCF_SUCCESS) ++ agent.add_action("validate-all", timeout=20, handler=validate_action) ++ agent.add_action("monitor", timeout=240, interval=10, handler=monitor_action) ++ setLoglevel(ocf.is_true(ocf.get_parameter("verbose", "false"))) ++ agent.run() ++ ++if __name__ == '__main__': ++ main() +\ No newline at end of file + +From a95337d882c7cc69d604b050159ad50b679f18be Mon Sep 17 00:00:00 2001 +From: MSSedusch +Date: Thu, 2 Jun 2022 14:10:33 +0200 +Subject: [PATCH 2/2] Remove developer documentation + +--- + heartbeat/azure-events-az.in | 11 ----------- + 1 file changed, 11 deletions(-) + +diff --git a/heartbeat/azure-events-az.in b/heartbeat/azure-events-az.in +index 616fc8d9e..59d095306 100644 +--- a/heartbeat/azure-events-az.in ++++ b/heartbeat/azure-events-az.in +@@ -723,17 +723,6 @@ description = ( + If any relevant events are found, it moves all Pacemaker resources + away from the affected node to allow for a graceful shutdown. + +- Usage: +- [OCF_RESKEY_eventTypes=VAL] [OCF_RESKEY_verbose=VAL] azure-events-az ACTION +- +- action (required): Supported values: monitor, help, meta-data +- eventTypes (optional): List of event types to be considered +- relevant by the resource agent (comma-separated). +- Supported values: Freeze,Reboot,Redeploy +- Default = Reboot,Redeploy +-/ verbose (optional): If set to true, displays debug info. +- Default = false +- + Deployment: + crm configure primitive rsc_azure-events-az ocf:heartbeat:azure-events-az \ + op monitor interval=10s diff --git a/bz2134539-IPsrcaddr-proto-metric-scope-default-route-fixes.patch b/bz2134536-IPsrcaddr-proto-metric-scope-default-route-fixes.patch similarity index 100% rename from bz2134539-IPsrcaddr-proto-metric-scope-default-route-fixes.patch rename to bz2134536-IPsrcaddr-proto-metric-scope-default-route-fixes.patch diff --git a/dist b/dist index 0ee7539..535c690 100644 --- a/dist +++ b/dist @@ -1 +1 @@ -an8_6 +an8_7 diff --git a/download b/download index 25e6580..7a32da4 100644 --- a/download +++ b/download @@ -5,5 +5,6 @@ d4095b09cdcea73400fbc5f2e81c24f1 aliyun-python-sdk-vpc-3.0.2.tar.gz a78cfc5987647f24c69d0b067f82d86a ClusterLabs-resource-agents-55a4e2c9.tar.gz 395439c56c4a74878f3ecf4b331575e3 colorama-0.3.3.tar.gz db084e239f24790564d8bb938c998cb4 google-cloud-sdk-360.0.0-linux-x86_64.tar.gz +47f6d2dc7904fc3e5bcdd79b44dde844 httplib2-0.20.4.tar.gz 024b338d1df5d5c835f95d0d5569d40d pycryptodome-3.6.4.tar.gz 71d24ed8b8c710fb1067ac0fba4b08b4 pyroute2-0.4.13.tar.gz diff --git a/pyparsing-2.4.7-py2.py3-none-any.whl b/pyparsing-2.4.7-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..773bcf62796e7462a0b94e8cb1f1bea67fb24baf GIT binary patch literal 67842 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 literal 0 HcmV?d00001 diff --git a/resource-agents.spec b/resource-agents.spec index b3d0200..038dced 100644 --- a/resource-agents.spec +++ b/resource-agents.spec @@ -33,6 +33,9 @@ %global pyroute2 pyroute2 %global pyroute2_version 0.4.13 %global pyroute2_dir %{bundled_lib_dir}/gcp/%{pyroute2} +# python-httplib2 bundle +%global httplib2 httplib2 +%global httplib2_version 0.20.4 ## alibaba cloud # python-colorama bundle %global colorama colorama @@ -66,7 +69,7 @@ Name: resource-agents Summary: Open Source HA Reusable Cluster Resource Scripts Version: 4.9.0 -Release: 16%{?rcver:%{rcver}}%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist}.5 +Release: 29%{?rcver:%{rcver}}%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist}.2 License: GPLv2+ and LGPLv2+ URL: https://github.com/ClusterLabs/resource-agents %if 0%{?fedora} || 0%{?centos_version} || 0%{?rhel} @@ -77,12 +80,14 @@ Group: Productivity/Clustering/HA Source0: %{upstream_prefix}-%{upstream_version}.tar.gz Source1: %{googlecloudsdk}-%{googlecloudsdk_version}-linux-x86_64.tar.gz Source2: %{pyroute2}-%{pyroute2_version}.tar.gz -Source3: %{colorama}-%{colorama_version}.tar.gz -Source4: %{pycryptodome}-%{pycryptodome_version}.tar.gz -Source5: %{aliyunsdkcore}-%{aliyunsdkcore_version}.tar.gz -Source6: %{aliyunsdkecs}-%{aliyunsdkecs_version}.tar.gz -Source7: %{aliyunsdkvpc}-%{aliyunsdkvpc_version}.tar.gz -Source8: %{aliyuncli}-%{aliyuncli_version}.tar.gz +Source3: pyparsing-2.4.7-py2.py3-none-any.whl +Source4: %{httplib2}-%{httplib2_version}.tar.gz +Source5: %{colorama}-%{colorama_version}.tar.gz +Source6: %{pycryptodome}-%{pycryptodome_version}.tar.gz +Source7: %{aliyunsdkcore}-%{aliyunsdkcore_version}.tar.gz +Source8: %{aliyunsdkecs}-%{aliyunsdkecs_version}.tar.gz +Source9: %{aliyunsdkvpc}-%{aliyunsdkvpc_version}.tar.gz +Source10: %{aliyuncli}-%{aliyuncli_version}.tar.gz Patch0: nova-compute-wait-NovaEvacuate.patch Patch1: bz1872754-pgsqlms-new-ra.patch Patch2: bz1995178-storage-mon-fix-typo.patch @@ -97,12 +102,26 @@ Patch10: bz2012057-Route-return-OCF_NOT_RUNNING-missing-route.patch Patch11: bz2029706-1-db2-crm_attribute-use-forever.patch Patch12: bz2029706-2-db2-fixes.patch Patch13: bz1992661-mysql-use-ssl-mode.patch -Patch14: bz1654862-1-IPsrcaddr-dhcp-warning.patch -Patch15: bz1654862-2-IPsrcaddr-error-message-route-not-found.patch -Patch16: bz1654862-3-IPsrcaddr-fix-indentation.patch -Patch17: bz1654862-4-IPsrcaddr-fixes.patch -Patch18: bz2092262-CTDB-move-process-to-root-cgroup-if-rt-enabled.patch -Patch19: bz2134539-IPsrcaddr-proto-metric-scope-default-route-fixes.patch +Patch14: bz2064342-1-IPsrcaddr-dhcp-warning.patch +Patch15: bz2064342-2-IPsrcaddr-error-message-route-not-found.patch +Patch16: bz2064342-3-IPsrcaddr-fix-indentation.patch +Patch17: bz2064342-4-IPsrcaddr-fixes.patch +Patch18: bz1908146-bz1908147-bz1908148-bz1949114-update-openstack-agents.patch +Patch19: bz2072043-LVM-activate-fix-fence-issue.patch +Patch20: bz2049414-Filesystem-1-fix-uuid-label-device-whitespace.patch +Patch21: bz2049414-Filesystem-2-improve-uuid-label-device-logic.patch +Patch22: bz2086889-lvmlockd-fail-when-use_lvmlockd-not-set.patch +Patch23: bz2093214-aws-vpc-move-ip-add-interface-label-support.patch +Patch24: bz1908148-openstack-info-fix-bashism.patch +Patch25: bz1908146-bz1908147-bz1949114-openstack-agents-fixes.patch +Patch26: bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-warn-when-openstackcli-slow.patch +Patch27: bz2103370-ocf-tester-1-update.patch +Patch28: bz2103370-ocf-tester-2-remove-deprecated-lrmd-lrmadmin-code.patch +Patch29: bz1908146-bz1908147-bz1908148-bz1949114-openstack-agents-set-domain-parameters-default.patch +Patch30: bz2090370-CTDB-move-process-to-root-cgroup-if-rt-enabled.patch +Patch31: bz2116941-ethmonitor-ovsmonitor-pgsql-fix-attrd_updater-q.patch +Patch32: bz2130986-azure-events-az-new-ra.patch +Patch33: bz2134536-IPsrcaddr-proto-metric-scope-default-route-fixes.patch # bundle patches Patch1000: 7-gcp-bundled.patch @@ -126,6 +145,10 @@ BuildRequires: libxslt glib2-devel BuildRequires: systemd BuildRequires: which +%ifarch x86_64 +BuildRequires: python3-pip +%endif + %if 0%{?fedora} || 0%{?centos_version} || 0%{?rhel} #BuildRequires: cluster-glue-libs-devel BuildRequires: docbook-style-xsl docbook-dtds @@ -249,6 +272,8 @@ Provides: bundled(python-websocket) = 0.47.0 Provides: bundled(python-yaml) = 3.12 # python-pyroute2 bundle Provides: bundled(%{pyroute2}) = %{pyroute2_version} +# python-httplib2 bundle +Provides: bundled(%{httplib2}) = %{httplib2_version} %description gcp The Google Cloud Platform resource agents allows Google Cloud @@ -296,6 +321,20 @@ exit 1 %patch17 -p1 %patch18 -p1 %patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 chmod 755 heartbeat/nova-compute-wait chmod 755 heartbeat/NovaEvacuate @@ -309,13 +348,13 @@ mkdir -p %{bundled_lib_dir}/aliyun %ifarch x86_64 tar -xzf %SOURCE1 -C %{bundled_lib_dir}/gcp # gcp*: append bundled-directory to search path, gcloud-ra -%patch1000 -p1 -F2 +%patch1000 -p1 # replace python-rsa with python-cryptography %patch1001 -p1 # gcloud support info %patch1002 -p1 # configure: skip bundled gcp lib checks -%patch1003 -p1 +%patch1003 -p1 -F1 # gcloud remove python 2 detection %patch1004 -p1 # rename gcloud @@ -387,7 +426,7 @@ cp %{pyroute2_dir}/LICENSE.Apache.v2 %{pyroute2}_LICENSE.Apache.v2 cp %{pyroute2_dir}/LICENSE.GPL.v2 %{pyroute2}_LICENSE.GPL.v2 # python-colorama bundle -tar -xzf %SOURCE3 -C %{bundled_lib_dir}/aliyun +tar -xzf %SOURCE5 -C %{bundled_lib_dir}/aliyun mv %{bundled_lib_dir}/aliyun/%{colorama}-%{colorama_version} %{colorama_dir} cp %{colorama_dir}/LICENSE.txt %{colorama}_LICENSE.txt cp %{colorama_dir}/README.rst %{colorama}_README.rst @@ -398,28 +437,28 @@ rm -rf *.egg-info popd # python-pycryptodome bundle -tar -xzf %SOURCE4 -C %{bundled_lib_dir}/aliyun +tar -xzf %SOURCE6 -C %{bundled_lib_dir}/aliyun mv %{bundled_lib_dir}/aliyun/%{pycryptodome}-%{pycryptodome_version} %{pycryptodome_dir} cp %{pycryptodome_dir}/README.rst %{pycryptodome}_README.rst cp %{pycryptodome_dir}/LICENSE.rst %{pycryptodome}_LICENSE.rst # python-aliyun-sdk-core bundle -tar -xzf %SOURCE5 -C %{bundled_lib_dir}/aliyun +tar -xzf %SOURCE7 -C %{bundled_lib_dir}/aliyun mv %{bundled_lib_dir}/aliyun/%{aliyunsdkcore}-%{aliyunsdkcore_version} %{aliyunsdkcore_dir} cp %{aliyunsdkcore_dir}/README.rst %{aliyunsdkcore}_README.rst # python-aliyun-sdk-ecs bundle -tar -xzf %SOURCE6 -C %{bundled_lib_dir}/aliyun +tar -xzf %SOURCE8 -C %{bundled_lib_dir}/aliyun mv %{bundled_lib_dir}/aliyun/%{aliyunsdkecs}-%{aliyunsdkecs_version} %{aliyunsdkecs_dir} cp %{aliyunsdkecs_dir}/README.rst %{aliyunsdkecs}_README.rst # python-aliyun-sdk-vpc bundle -tar -xzf %SOURCE7 -C %{bundled_lib_dir}/aliyun +tar -xzf %SOURCE9 -C %{bundled_lib_dir}/aliyun mv %{bundled_lib_dir}/aliyun/%{aliyunsdkvpc}-%{aliyunsdkvpc_version} %{aliyunsdkvpc_dir} cp %{aliyunsdkvpc_dir}/README.rst %{aliyunsdkvpc}_README.rst # aliyuncli bundle -tar -xzf %SOURCE8 -C %{bundled_lib_dir}/aliyun +tar -xzf %SOURCE10 -C %{bundled_lib_dir}/aliyun mv %{bundled_lib_dir}/aliyun/%{aliyuncli}-%{aliyuncli_version} %{aliyuncli_dir} cp %{aliyuncli_dir}/README.rst %{aliyuncli}_README.rst cp %{aliyuncli_dir}/LICENSE %{aliyuncli}_LICENSE @@ -541,6 +580,10 @@ pushd %{pyroute2_dir} %{__python3} setup.py install -O1 --skip-build --root %{buildroot} --install-lib /usr/lib/%{name}/%{bundled_lib_dir}/gcp popd +# python-httplib2 bundle +%{__python3} -m pip install --user --no-index --find-links %{_sourcedir} pyparsing +%{__python3} -m pip install --target %{buildroot}/usr/lib/%{name}/%{bundled_lib_dir}/gcp --no-index --find-links %{_sourcedir} %{httplib2} + # python-colorama bundle pushd %{colorama_dir} %{__python3} setup.py install -O1 --skip-build --root %{buildroot} --install-lib /usr/lib/%{name}/%{bundled_lib_dir}/aliyun @@ -636,6 +679,7 @@ rm -rf %{buildroot} %exclude %{_datadir}/%{name}/ocft/runocft %exclude %{_datadir}/%{name}/ocft/runocft.prereq +%{_sbindir}/ocf-tester %{_sbindir}/ocft %{_includedir}/heartbeat @@ -647,6 +691,7 @@ rm -rf %{buildroot} %endif %{_mandir}/man7/*.7* +%{_mandir}/man8/ocf-tester.8* ### # Supported, but in another sub package @@ -718,10 +763,6 @@ rm -rf %{buildroot} %exclude %{_usr}/lib/ocf/resource.d/heartbeat/mpathpersist %exclude %{_usr}/lib/ocf/resource.d/heartbeat/mysql-proxy %exclude %{_usr}/lib/ocf/resource.d/heartbeat/nvmet-* -%exclude %{_usr}/lib/ocf/resource.d/heartbeat/openstack-cinder-volume -%exclude %{_usr}/lib/ocf/resource.d/heartbeat/openstack-floating-ip -%exclude %{_usr}/lib/ocf/resource.d/heartbeat/openstack-info -%exclude %{_usr}/lib/ocf/resource.d/heartbeat/openstack-virtual-ip %exclude %{_usr}/lib/ocf/resource.d/heartbeat/ovsmonitor %exclude %{_usr}/lib/ocf/resource.d/heartbeat/pgagent %exclude %{_usr}/lib/ocf/resource.d/heartbeat/pingd @@ -786,10 +827,6 @@ rm -rf %{buildroot} %exclude %{_mandir}/man7/ocf_heartbeat_mpathpersist.7.gz %exclude %{_mandir}/man7/ocf_heartbeat_mysql-proxy.7.gz %exclude %{_mandir}/man7/ocf_heartbeat_nvmet-*.7.gz -%exclude %{_mandir}/man7/ocf_heartbeat_openstack-cinder-volume.7.gz -%exclude %{_mandir}/man7/ocf_heartbeat_openstack-floating-ip.7.gz -%exclude %{_mandir}/man7/ocf_heartbeat_openstack-info.7.gz -%exclude %{_mandir}/man7/ocf_heartbeat_openstack-virtual-ip.7.gz %exclude %{_mandir}/man7/ocf_heartbeat_ovsmonitor.7.gz %exclude %{_mandir}/man7/ocf_heartbeat_pgagent.7.gz %exclude %{_mandir}/man7/ocf_heartbeat_pingd.7.gz @@ -810,9 +847,6 @@ rm -rf %{buildroot} ### # Other excluded files. ### -# This tool has to be updated for the new pacemaker lrmd. -%exclude %{_sbindir}/ocf-tester -%exclude %{_mandir}/man8/ocf-tester.8* # ldirectord is not supported %exclude /etc/ha.d/resource.d/ldirectord %exclude /etc/init.d/ldirectord @@ -876,21 +910,70 @@ ccs_update_schema > /dev/null 2>&1 ||: %{_usr}/lib/ocf/lib/heartbeat/OCF_*.pm %changelog -* Fri Oct 14 2022 Oyvind Albrigtsen - 4.9.0-16.5 +* Fri Oct 14 2022 Oyvind Albrigtsen - 4.9.0-29.2 - IPsrcaddr: proto, metric, scope and default route fixes - Resolves: rhbz#2134539 + Resolves: rhbz#2134536 + +* Mon Oct 3 2022 Oyvind Albrigtsen - 4.9.0-29.1 +- azure-events-az: new resource agent + + Resolves: rhbz#2130986 + +* Wed Aug 10 2022 Oyvind Albrigtsen - 4.9.0-29 +- ethmonitor/pgsql: remove attrd_updater "-q" parameter to solve issue + with Pacemaker 2.1.3+ not ignoring it -* Fri Aug 5 2022 Oyvind Albrigtsen - 4.9.0-16.2 + Resolves: rhbz#2116941 + +* Thu Aug 4 2022 Oyvind Albrigtsen - 4.9.0-28 - CTDB: move process to root cgroup if realtime scheduling is enabled - Resolves: rhbz#2092262 + Resolves: rhbz#2090370 + +* Thu Jul 14 2022 Oyvind Albrigtsen - 4.9.0-27 +- ocf-tester: add testing tool + + Resolves: rhbz#2103370 + +* Thu Jul 14 2022 Oyvind Albrigtsen - 4.9.0-26 +- openstack-cinder-volume/openstack-floating-ip/openstack-info/ + openstack-virtual-ip: new resource agents + + Resolves: rhbz#1908146, rhbz#1908147, rhbz#1908148, rhbz#1949114 + +* Thu Jun 16 2022 Oyvind Albrigtsen - 4.9.0-22 +- gcp-vpc-move-route/gcp-vpc-move-vip: upgrade bundled + python-httplib2 to fix SSL issue + + Resolves: rhbz#2097462 + +* Thu Jun 9 2022 Oyvind Albrigtsen - 4.9.0-21 +- aws-vpc-move-ip: add interface label support + + Resolves: rhbz#2093214 + +* Wed Jun 8 2022 Oyvind Albrigtsen - 4.9.0-20 +- lvmlockd: fail when use_lvmlockd has not been set + + Resolves: rhbz#2086889 + +* Thu Apr 21 2022 Oyvind Albrigtsen - 4.9.0-19 +- Filesystem: fix UUID/label device support when there's whitespace + between parameter and UUID/label + + Resolves: rhbz#2049414 + +* Thu Apr 7 2022 Oyvind Albrigtsen - 4.9.0-18 +- LVM-activate: use correct return code to fence failed node + + Resolves: rhbz#2072043 * Thu Mar 3 2022 Oyvind Albrigtsen - 4.9.0-16 - IPsrcaddr: add warning about possible issues when used with DHCP, and add error message when matching route not found - Resolves: rhbz#1654862 + Resolves: rhbz#2064342 * Thu Feb 24 2022 Oyvind Albrigtsen - 4.9.0-15 - db2: use -l forever to fix crm_attribute issue -- Gitee