Blob Blame History Raw
From a467f0953c61bd56a9b34a98c71855d3cfbf6ba4 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 5 Apr 2022 16:26:30 -0500
Subject: [PATCH 01/14] Refactor: tools: use a flag to indicate locked
 resources in crm_resource

... to make the handling consistent with other checks. This also allows some of
the code to be simplified.
---
 tools/crm_resource.h         | 13 +++++++++----
 tools/crm_resource_print.c   | 21 ++++++++-------------
 tools/crm_resource_runtime.c |  7 +++----
 3 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/tools/crm_resource.h b/tools/crm_resource.h
index 71a978893..b5fdd1bb5 100644
--- a/tools/crm_resource.h
+++ b/tools/crm_resource.h
@@ -8,6 +8,10 @@
  */
 
 #include <crm_internal.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
 #include <crm/crm.h>
 
 #include <crm/msg_xml.h>
@@ -31,13 +35,14 @@ typedef struct node_info_s {
 enum resource_check_flags {
     rsc_remain_stopped  = (1 << 0),
     rsc_unpromotable    = (1 << 1),
-    rsc_unmanaged       = (1 << 2)
+    rsc_unmanaged       = (1 << 2),
+    rsc_locked          = (1 << 3),
 };
 
 typedef struct resource_checks_s {
-    pe_resource_t *rsc;
-    unsigned int flags;
-    const char *lock_node;
+    pe_resource_t *rsc;     // Resource being checked
+    uint32_t flags;         // Group of enum resource_check_flags
+    const char *lock_node;  // Node that resource is shutdown-locked to, if any
 } resource_checks_t;
 
 resource_checks_t *cli_check_resource(pe_resource_t *rsc, char *role_s, char *managed);
diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
index 5abf3df0c..f63fc952d 100644
--- a/tools/crm_resource_print.c
+++ b/tools/crm_resource_print.c
@@ -450,14 +450,13 @@ resource_check_list_default(pcmk__output_t *out, va_list args) {
     resource_checks_t *checks = va_arg(args, resource_checks_t *);
 
     pe_resource_t *parent = uber_parent(checks->rsc);
-    int rc = pcmk_rc_no_output;
-    bool printed = false;
 
-    if (checks->flags != 0 || checks->lock_node != NULL) {
-        printed = true;
-        out->begin_list(out, NULL, NULL, "Resource Checks");
+    if (checks->flags == 0) {
+        return pcmk_rc_no_output;
     }
 
+    out->begin_list(out, NULL, NULL, "Resource Checks");
+
     if (pcmk_is_set(checks->flags, rsc_remain_stopped)) {
         out->list_item(out, "check", "Configuration specifies '%s' should remain stopped",
                        parent->id);
@@ -473,17 +472,13 @@ resource_check_list_default(pcmk__output_t *out, va_list args) {
                        parent->id);
     }
 
-    if (checks->lock_node) {
+    if (pcmk_is_set(checks->flags, rsc_locked)) {
         out->list_item(out, "check", "'%s' is locked to node %s due to shutdown",
                        parent->id, checks->lock_node);
     }
 
-    if (printed) {
-        out->end_list(out);
-        rc = pcmk_rc_ok;
-    }
-
-    return rc;
+    out->end_list(out);
+    return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *")
@@ -509,7 +504,7 @@ resource_check_list_xml(pcmk__output_t *out, va_list args) {
         pcmk__xe_set_bool_attr(node, "unmanaged", true);
     }
 
-    if (checks->lock_node) {
+    if (pcmk_is_set(checks->flags, rsc_locked)) {
         crm_xml_add(node, "locked-to", checks->lock_node);
     }
 
diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index 9e7e1fe74..b5bccadaf 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -36,7 +36,8 @@ cli_check_resource(pe_resource_t *rsc, char *role_s, char *managed)
         rc->flags |= rsc_unmanaged;
     }
 
-    if (rsc->lock_node) {
+    if (rsc->lock_node != NULL) {
+        rc->flags |= rsc_locked;
         rc->lock_node = rsc->lock_node->details->uname;
     }
 
@@ -914,9 +915,7 @@ cli_resource_check(pcmk__output_t *out, cib_t * cib_conn, pe_resource_t *rsc)
 
     checks = cli_check_resource(rsc, role_s, managed);
 
-    if (checks->flags != 0 || checks->lock_node != NULL) {
-        rc = out->message(out, "resource-check-list", checks);
-    }
+    rc = out->message(out, "resource-check-list", checks);
 
     free(role_s);
     free(managed);
-- 
2.31.1


From 7f8f94d0a1086e592e39f3a1a812b1a65225c09b Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 5 Apr 2022 16:48:03 -0500
Subject: [PATCH 02/14] Refactor: tools: functionize individual resource checks
 in crm_resource

... rather than have one check-everything function, to make the code simpler
and more readable.
---
 tools/crm_resource_runtime.c | 101 ++++++++++++++++++++---------------
 1 file changed, 57 insertions(+), 44 deletions(-)

diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index b5bccadaf..d47f959f5 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -15,36 +15,6 @@
 #include <crm/common/lists_internal.h>
 #include <crm/services_internal.h>
 
-resource_checks_t *
-cli_check_resource(pe_resource_t *rsc, char *role_s, char *managed)
-{
-    pe_resource_t *parent = uber_parent(rsc);
-    resource_checks_t *rc = calloc(1, sizeof(resource_checks_t));
-
-    if (role_s) {
-        enum rsc_role_e role = text2role(role_s);
-
-        if (role == RSC_ROLE_STOPPED) {
-            rc->flags |= rsc_remain_stopped;
-        } else if (pcmk_is_set(parent->flags, pe_rsc_promotable) &&
-                   (role == RSC_ROLE_UNPROMOTED)) {
-            rc->flags |= rsc_unpromotable;
-        }
-    }
-
-    if (managed && !crm_is_true(managed)) {
-        rc->flags |= rsc_unmanaged;
-    }
-
-    if (rsc->lock_node != NULL) {
-        rc->flags |= rsc_locked;
-        rc->lock_node = rsc->lock_node->details->uname;
-    }
-
-    rc->rsc = rsc;
-    return rc;
-}
-
 static GList *
 build_node_info_list(pe_resource_t *rsc)
 {
@@ -898,29 +868,72 @@ cli_cleanup_all(pcmk_ipc_api_t *controld_api, const char *node_name,
     return rc;
 }
 
-int
-cli_resource_check(pcmk__output_t *out, cib_t * cib_conn, pe_resource_t *rsc)
+static void
+check_role(pcmk__output_t *out, cib_t *cib_conn, resource_checks_t *checks)
 {
     char *role_s = NULL;
-    char *managed = NULL;
-    pe_resource_t *parent = uber_parent(rsc);
-    int rc = pcmk_rc_no_output;
-    resource_checks_t *checks = NULL;
-
-    find_resource_attr(out, cib_conn, XML_NVPAIR_ATTR_VALUE, parent->id,
-                       NULL, NULL, NULL, XML_RSC_ATTR_MANAGED, &managed);
+    pe_resource_t *parent = uber_parent(checks->rsc);
 
     find_resource_attr(out, cib_conn, XML_NVPAIR_ATTR_VALUE, parent->id,
                        NULL, NULL, NULL, XML_RSC_ATTR_TARGET_ROLE, &role_s);
+    if (role_s == NULL) {
+        return;
+    }
 
-    checks = cli_check_resource(rsc, role_s, managed);
+    switch (text2role(role_s)) {
+        case RSC_ROLE_STOPPED:
+            checks->flags |= rsc_remain_stopped;
+            break;
 
-    rc = out->message(out, "resource-check-list", checks);
+        case RSC_ROLE_UNPROMOTED:
+            if (pcmk_is_set(parent->flags, pe_rsc_promotable)) {
+                checks->flags |= rsc_unpromotable;
+            }
+            break;
 
+        default:
+            break;
+    }
     free(role_s);
-    free(managed);
-    free(checks);
-    return rc;
+}
+
+static void
+check_managed(pcmk__output_t *out, cib_t *cib_conn, resource_checks_t *checks)
+{
+    char *managed_s = NULL;
+    pe_resource_t *parent = uber_parent(checks->rsc);
+
+    find_resource_attr(out, cib_conn, XML_NVPAIR_ATTR_VALUE, parent->id,
+                       NULL, NULL, NULL, XML_RSC_ATTR_MANAGED, &managed_s);
+    if (managed_s == NULL) {
+        return;
+    }
+
+    if (!crm_is_true(managed_s)) {
+        checks->flags |= rsc_unmanaged;
+    }
+    free(managed_s);
+}
+
+static void
+check_locked(resource_checks_t *checks)
+{
+    if (checks->rsc->lock_node != NULL) {
+        checks->flags |= rsc_locked;
+        checks->lock_node = checks->rsc->lock_node->details->uname;
+    }
+}
+
+int
+cli_resource_check(pcmk__output_t *out, cib_t * cib_conn, pe_resource_t *rsc)
+{
+    resource_checks_t checks = { .rsc = rsc };
+
+    check_role(out, cib_conn, &checks);
+    check_managed(out, cib_conn, &checks);
+    check_locked(&checks);
+
+    return out->message(out, "resource-check-list", &checks);
 }
 
 // \return Standard Pacemaker return code
-- 
2.31.1


From 32414475281d909cd808f723a41d88a5e0d2b254 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 5 Apr 2022 17:11:07 -0500
Subject: [PATCH 03/14] Fix: tools: crm_resource target-role check should use
 meta-attribute table

Previously, check_role() searched the CIB for the uber-parent's target-role
attribute. That could give incorrect results if target-role was set on a
different resource in the ancestry chain (e.g. the resource itself for a group
member, or the group for a cloned group), or if there were multiple target-role
settings (e.g. using rules).

Now, target-role is checked in rsc->meta, which should be fully evaluated for
inheritance and rules.
---
 tools/crm_resource_runtime.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index d47f959f5..e9d05cb77 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -869,24 +869,22 @@ cli_cleanup_all(pcmk_ipc_api_t *controld_api, const char *node_name,
 }
 
 static void
-check_role(pcmk__output_t *out, cib_t *cib_conn, resource_checks_t *checks)
+check_role(resource_checks_t *checks)
 {
-    char *role_s = NULL;
-    pe_resource_t *parent = uber_parent(checks->rsc);
+    const char *role_s = g_hash_table_lookup(checks->rsc->meta,
+                                             XML_RSC_ATTR_TARGET_ROLE);
 
-    find_resource_attr(out, cib_conn, XML_NVPAIR_ATTR_VALUE, parent->id,
-                       NULL, NULL, NULL, XML_RSC_ATTR_TARGET_ROLE, &role_s);
     if (role_s == NULL) {
         return;
     }
-
     switch (text2role(role_s)) {
         case RSC_ROLE_STOPPED:
             checks->flags |= rsc_remain_stopped;
             break;
 
         case RSC_ROLE_UNPROMOTED:
-            if (pcmk_is_set(parent->flags, pe_rsc_promotable)) {
+            if (pcmk_is_set(uber_parent(checks->rsc)->flags,
+                            pe_rsc_promotable)) {
                 checks->flags |= rsc_unpromotable;
             }
             break;
@@ -894,7 +892,6 @@ check_role(pcmk__output_t *out, cib_t *cib_conn, resource_checks_t *checks)
         default:
             break;
     }
-    free(role_s);
 }
 
 static void
@@ -929,7 +926,7 @@ cli_resource_check(pcmk__output_t *out, cib_t * cib_conn, pe_resource_t *rsc)
 {
     resource_checks_t checks = { .rsc = rsc };
 
-    check_role(out, cib_conn, &checks);
+    check_role(&checks);
     check_managed(out, cib_conn, &checks);
     check_locked(&checks);
 
-- 
2.31.1


From 0fd133680f7b2c25a946cf3fb25f4ee9ffeeaf93 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 5 Apr 2022 17:15:43 -0500
Subject: [PATCH 04/14] Fix: tools: crm_resource is-managed check should use
 meta-attribute table

Previously, check_managed() searched the CIB for the uber-parent's is-managed
attribute. That could give incorrect results if is-managed was set on a
different resource in the ancestry chain (e.g. the resource itself for a group
member, or the group for a cloned group), or if there were multiple is-managed
settings (e.g. using rules).

Now, is-managed is checked in rsc->meta, which should be fully evaluated for
inheritance and rules.
---
 tools/crm_resource_runtime.c | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index e9d05cb77..4f62b4b2e 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -895,21 +895,14 @@ check_role(resource_checks_t *checks)
 }
 
 static void
-check_managed(pcmk__output_t *out, cib_t *cib_conn, resource_checks_t *checks)
+check_managed(resource_checks_t *checks)
 {
-    char *managed_s = NULL;
-    pe_resource_t *parent = uber_parent(checks->rsc);
+    const char *managed_s = g_hash_table_lookup(checks->rsc->meta,
+                                                XML_RSC_ATTR_MANAGED);
 
-    find_resource_attr(out, cib_conn, XML_NVPAIR_ATTR_VALUE, parent->id,
-                       NULL, NULL, NULL, XML_RSC_ATTR_MANAGED, &managed_s);
-    if (managed_s == NULL) {
-        return;
-    }
-
-    if (!crm_is_true(managed_s)) {
+    if ((managed_s != NULL) && !crm_is_true(managed_s)) {
         checks->flags |= rsc_unmanaged;
     }
-    free(managed_s);
 }
 
 static void
@@ -927,7 +920,7 @@ cli_resource_check(pcmk__output_t *out, cib_t * cib_conn, pe_resource_t *rsc)
     resource_checks_t checks = { .rsc = rsc };
 
     check_role(&checks);
-    check_managed(out, cib_conn, &checks);
+    check_managed(&checks);
     check_locked(&checks);
 
     return out->message(out, "resource-check-list", &checks);
-- 
2.31.1


From e9523c1b238492c8cf8b453ba6710f13bf81cd28 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 5 Apr 2022 17:18:44 -0500
Subject: [PATCH 05/14] Refactor: tools: drop unused argument from
 cli_resource_check()

---
 tools/crm_resource.c         |  4 ++--
 tools/crm_resource.h         |  2 +-
 tools/crm_resource_print.c   | 24 ++++++++++++------------
 tools/crm_resource_runtime.c |  2 +-
 4 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/tools/crm_resource.c b/tools/crm_resource.c
index 883563df9..bf5326b40 100644
--- a/tools/crm_resource.c
+++ b/tools/crm_resource.c
@@ -1019,7 +1019,7 @@ cleanup(pcmk__output_t *out, pe_resource_t *rsc)
 
     if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
         // Show any reasons why resource might stay stopped
-        cli_resource_check(out, cib_conn, rsc);
+        cli_resource_check(out, rsc);
     }
 
     if (rc == pcmk_rc_ok) {
@@ -1326,7 +1326,7 @@ refresh_resource(pcmk__output_t *out, pe_resource_t *rsc)
 
     if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
         // Show any reasons why resource might stay stopped
-        cli_resource_check(out, cib_conn, rsc);
+        cli_resource_check(out, rsc);
     }
 
     if (rc == pcmk_rc_ok) {
diff --git a/tools/crm_resource.h b/tools/crm_resource.h
index b5fdd1bb5..bcff2b5f6 100644
--- a/tools/crm_resource.h
+++ b/tools/crm_resource.h
@@ -68,7 +68,7 @@ int cli_resource_print_operations(const char *rsc_id, const char *host_uname,
                                   bool active, pe_working_set_t * data_set);
 
 /* runtime */
-int cli_resource_check(pcmk__output_t *out, cib_t * cib, pe_resource_t *rsc);
+int cli_resource_check(pcmk__output_t *out, pe_resource_t *rsc);
 int cli_resource_fail(pcmk_ipc_api_t *controld_api, const char *host_uname,
                       const char *rsc_id, pe_working_set_t *data_set);
 GList *cli_resource_search(pe_resource_t *rsc, const char *requested_name,
diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
index f63fc952d..f025cbddd 100644
--- a/tools/crm_resource_print.c
+++ b/tools/crm_resource_print.c
@@ -587,7 +587,7 @@ PCMK__OUTPUT_ARGS("resource-reasons-list", "cib_t *", "GList *", "pe_resource_t
 static int
 resource_reasons_list_default(pcmk__output_t *out, va_list args)
 {
-    cib_t *cib_conn = va_arg(args, cib_t *);
+    cib_t *cib_conn G_GNUC_UNUSED = va_arg(args, cib_t *);
     GList *resources = va_arg(args, GList *);
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     pe_node_t *node = va_arg(args, pe_node_t *);
@@ -610,7 +610,7 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
                 out->list_item(out, "reason", "Resource %s is running", rsc->id);
             }
 
-            cli_resource_check(out, cib_conn, rsc);
+            cli_resource_check(out, rsc);
             g_list_free(hosts);
             hosts = NULL;
         }
@@ -624,7 +624,7 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
                            rsc->id, host_uname);
         }
 
-        cli_resource_check(out, cib_conn, rsc);
+        cli_resource_check(out, rsc);
 
     } else if ((rsc == NULL) && (host_uname != NULL)) {
         const char* host_uname =  node->details->uname;
@@ -637,14 +637,14 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
             pe_resource_t *rsc = (pe_resource_t *) lpc->data;
             out->list_item(out, "reason", "Resource %s is running on host %s",
                            rsc->id, host_uname);
-            cli_resource_check(out, cib_conn, rsc);
+            cli_resource_check(out, rsc);
         }
 
         for(lpc = unactiveResources; lpc != NULL; lpc = lpc->next) {
             pe_resource_t *rsc = (pe_resource_t *) lpc->data;
             out->list_item(out, "reason", "Resource %s is assigned to host %s but not running",
                            rsc->id, host_uname);
-            cli_resource_check(out, cib_conn, rsc);
+            cli_resource_check(out, rsc);
         }
 
         g_list_free(allResources);
@@ -657,7 +657,7 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
         rsc->fns->location(rsc, &hosts, TRUE);
         out->list_item(out, "reason", "Resource %s is %srunning",
                        rsc->id, (hosts? "" : "not "));
-        cli_resource_check(out, cib_conn, rsc);
+        cli_resource_check(out, rsc);
         g_list_free(hosts);
     }
 
@@ -670,7 +670,7 @@ PCMK__OUTPUT_ARGS("resource-reasons-list", "cib_t *", "GList *", "pe_resource_t
 static int
 resource_reasons_list_xml(pcmk__output_t *out, va_list args)
 {
-    cib_t *cib_conn = va_arg(args, cib_t *);
+    cib_t *cib_conn G_GNUC_UNUSED = va_arg(args, cib_t *);
     GList *resources = va_arg(args, GList *);
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     pe_node_t *node = va_arg(args, pe_node_t *);
@@ -695,7 +695,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
                                            "running", pcmk__btoa(hosts != NULL),
                                            NULL);
 
-            cli_resource_check(out, cib_conn, rsc);
+            cli_resource_check(out, rsc);
             pcmk__output_xml_pop_parent(out);
             g_list_free(hosts);
             hosts = NULL;
@@ -708,7 +708,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
             crm_xml_add(xml_node, "running_on", host_uname);
         }
 
-        cli_resource_check(out, cib_conn, rsc);
+        cli_resource_check(out, rsc);
 
     } else if ((rsc == NULL) && (host_uname != NULL)) {
         const char* host_uname =  node->details->uname;
@@ -728,7 +728,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
                                            "host", host_uname,
                                            NULL);
 
-            cli_resource_check(out, cib_conn, rsc);
+            cli_resource_check(out, rsc);
             pcmk__output_xml_pop_parent(out);
         }
 
@@ -741,7 +741,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
                                            "host", host_uname,
                                            NULL);
 
-            cli_resource_check(out, cib_conn, rsc);
+            cli_resource_check(out, rsc);
             pcmk__output_xml_pop_parent(out);
         }
 
@@ -755,7 +755,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
 
         rsc->fns->location(rsc, &hosts, TRUE);
         crm_xml_add(xml_node, "running", pcmk__btoa(hosts != NULL));
-        cli_resource_check(out, cib_conn, rsc);
+        cli_resource_check(out, rsc);
         g_list_free(hosts);
     }
 
diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index 4f62b4b2e..47653a060 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -915,7 +915,7 @@ check_locked(resource_checks_t *checks)
 }
 
 int
-cli_resource_check(pcmk__output_t *out, cib_t * cib_conn, pe_resource_t *rsc)
+cli_resource_check(pcmk__output_t *out, pe_resource_t *rsc)
 {
     resource_checks_t checks = { .rsc = rsc };
 
-- 
2.31.1


From b1a1a07f3e44bc74575eab325277ea8c1f3391b2 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 5 Apr 2022 17:20:06 -0500
Subject: [PATCH 06/14] Refactor: tools: drop unused argument from
 resource-reasons-list message

---
 tools/crm_resource.c       | 2 +-
 tools/crm_resource_print.c | 6 ++----
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/tools/crm_resource.c b/tools/crm_resource.c
index bf5326b40..7f656a20d 100644
--- a/tools/crm_resource.c
+++ b/tools/crm_resource.c
@@ -1941,7 +1941,7 @@ main(int argc, char **argv)
             if ((options.host_uname != NULL) && (node == NULL)) {
                 rc = pcmk_rc_node_unknown;
             } else {
-                rc = out->message(out, "resource-reasons-list", cib_conn,
+                rc = out->message(out, "resource-reasons-list",
                                   data_set->resources, rsc, node);
             }
             break;
diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
index f025cbddd..580f9c71a 100644
--- a/tools/crm_resource_print.c
+++ b/tools/crm_resource_print.c
@@ -582,12 +582,11 @@ resource_search_list_xml(pcmk__output_t *out, va_list args)
     return pcmk_rc_ok;
 }
 
-PCMK__OUTPUT_ARGS("resource-reasons-list", "cib_t *", "GList *", "pe_resource_t *",
+PCMK__OUTPUT_ARGS("resource-reasons-list", "GList *", "pe_resource_t *",
                   "pe_node_t *")
 static int
 resource_reasons_list_default(pcmk__output_t *out, va_list args)
 {
-    cib_t *cib_conn G_GNUC_UNUSED = va_arg(args, cib_t *);
     GList *resources = va_arg(args, GList *);
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     pe_node_t *node = va_arg(args, pe_node_t *);
@@ -665,12 +664,11 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
     return pcmk_rc_ok;
 }
 
-PCMK__OUTPUT_ARGS("resource-reasons-list", "cib_t *", "GList *", "pe_resource_t *",
+PCMK__OUTPUT_ARGS("resource-reasons-list", "GList *", "pe_resource_t *",
                   "pe_node_t *")
 static int
 resource_reasons_list_xml(pcmk__output_t *out, va_list args)
 {
-    cib_t *cib_conn G_GNUC_UNUSED = va_arg(args, cib_t *);
     GList *resources = va_arg(args, GList *);
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     pe_node_t *node = va_arg(args, pe_node_t *);
-- 
2.31.1


From 973eb2694b334b4e9e6967f6c7ceaebec10693db Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 23 Jun 2022 10:08:37 -0500
Subject: [PATCH 07/14] Refactor: tools: pass node to cli_resource_check()

The node is not used yet
---
 tools/crm_resource.c         | 12 ++++++------
 tools/crm_resource.h         |  3 ++-
 tools/crm_resource_print.c   | 20 ++++++++++----------
 tools/crm_resource_runtime.c |  2 +-
 4 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/tools/crm_resource.c b/tools/crm_resource.c
index 7f656a20d..756a06268 100644
--- a/tools/crm_resource.c
+++ b/tools/crm_resource.c
@@ -1004,7 +1004,7 @@ ban_or_move(pcmk__output_t *out, pe_resource_t *rsc, const char *move_lifetime)
 }
 
 static void
-cleanup(pcmk__output_t *out, pe_resource_t *rsc)
+cleanup(pcmk__output_t *out, pe_resource_t *rsc, pe_node_t *node)
 {
     int rc = pcmk_rc_ok;
 
@@ -1019,7 +1019,7 @@ cleanup(pcmk__output_t *out, pe_resource_t *rsc)
 
     if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
         // Show any reasons why resource might stay stopped
-        cli_resource_check(out, rsc);
+        cli_resource_check(out, rsc, node);
     }
 
     if (rc == pcmk_rc_ok) {
@@ -1311,7 +1311,7 @@ refresh(pcmk__output_t *out)
 }
 
 static void
-refresh_resource(pcmk__output_t *out, pe_resource_t *rsc)
+refresh_resource(pcmk__output_t *out, pe_resource_t *rsc, pe_node_t *node)
 {
     int rc = pcmk_rc_ok;
 
@@ -1326,7 +1326,7 @@ refresh_resource(pcmk__output_t *out, pe_resource_t *rsc)
 
     if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
         // Show any reasons why resource might stay stopped
-        cli_resource_check(out, rsc);
+        cli_resource_check(out, rsc, node);
     }
 
     if (rc == pcmk_rc_ok) {
@@ -2075,7 +2075,7 @@ main(int argc, char **argv)
                     start_mainloop(controld_api);
                 }
             } else {
-                cleanup(out, rsc);
+                cleanup(out, rsc, node);
             }
             break;
 
@@ -2083,7 +2083,7 @@ main(int argc, char **argv)
             if (rsc == NULL) {
                 rc = refresh(out);
             } else {
-                refresh_resource(out, rsc);
+                refresh_resource(out, rsc, node);
             }
             break;
 
diff --git a/tools/crm_resource.h b/tools/crm_resource.h
index bcff2b5f6..f7e44476d 100644
--- a/tools/crm_resource.h
+++ b/tools/crm_resource.h
@@ -68,7 +68,8 @@ int cli_resource_print_operations(const char *rsc_id, const char *host_uname,
                                   bool active, pe_working_set_t * data_set);
 
 /* runtime */
-int cli_resource_check(pcmk__output_t *out, pe_resource_t *rsc);
+int cli_resource_check(pcmk__output_t *out, pe_resource_t *rsc,
+                       pe_node_t *node);
 int cli_resource_fail(pcmk_ipc_api_t *controld_api, const char *host_uname,
                       const char *rsc_id, pe_working_set_t *data_set);
 GList *cli_resource_search(pe_resource_t *rsc, const char *requested_name,
diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
index 580f9c71a..087819601 100644
--- a/tools/crm_resource_print.c
+++ b/tools/crm_resource_print.c
@@ -609,7 +609,7 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
                 out->list_item(out, "reason", "Resource %s is running", rsc->id);
             }
 
-            cli_resource_check(out, rsc);
+            cli_resource_check(out, rsc, NULL);
             g_list_free(hosts);
             hosts = NULL;
         }
@@ -623,7 +623,7 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
                            rsc->id, host_uname);
         }
 
-        cli_resource_check(out, rsc);
+        cli_resource_check(out, rsc, node);
 
     } else if ((rsc == NULL) && (host_uname != NULL)) {
         const char* host_uname =  node->details->uname;
@@ -636,14 +636,14 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
             pe_resource_t *rsc = (pe_resource_t *) lpc->data;
             out->list_item(out, "reason", "Resource %s is running on host %s",
                            rsc->id, host_uname);
-            cli_resource_check(out, rsc);
+            cli_resource_check(out, rsc, node);
         }
 
         for(lpc = unactiveResources; lpc != NULL; lpc = lpc->next) {
             pe_resource_t *rsc = (pe_resource_t *) lpc->data;
             out->list_item(out, "reason", "Resource %s is assigned to host %s but not running",
                            rsc->id, host_uname);
-            cli_resource_check(out, rsc);
+            cli_resource_check(out, rsc, node);
         }
 
         g_list_free(allResources);
@@ -656,7 +656,7 @@ resource_reasons_list_default(pcmk__output_t *out, va_list args)
         rsc->fns->location(rsc, &hosts, TRUE);
         out->list_item(out, "reason", "Resource %s is %srunning",
                        rsc->id, (hosts? "" : "not "));
-        cli_resource_check(out, rsc);
+        cli_resource_check(out, rsc, NULL);
         g_list_free(hosts);
     }
 
@@ -693,7 +693,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
                                            "running", pcmk__btoa(hosts != NULL),
                                            NULL);
 
-            cli_resource_check(out, rsc);
+            cli_resource_check(out, rsc, NULL);
             pcmk__output_xml_pop_parent(out);
             g_list_free(hosts);
             hosts = NULL;
@@ -706,7 +706,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
             crm_xml_add(xml_node, "running_on", host_uname);
         }
 
-        cli_resource_check(out, rsc);
+        cli_resource_check(out, rsc, node);
 
     } else if ((rsc == NULL) && (host_uname != NULL)) {
         const char* host_uname =  node->details->uname;
@@ -726,7 +726,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
                                            "host", host_uname,
                                            NULL);
 
-            cli_resource_check(out, rsc);
+            cli_resource_check(out, rsc, node);
             pcmk__output_xml_pop_parent(out);
         }
 
@@ -739,7 +739,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
                                            "host", host_uname,
                                            NULL);
 
-            cli_resource_check(out, rsc);
+            cli_resource_check(out, rsc, node);
             pcmk__output_xml_pop_parent(out);
         }
 
@@ -753,7 +753,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
 
         rsc->fns->location(rsc, &hosts, TRUE);
         crm_xml_add(xml_node, "running", pcmk__btoa(hosts != NULL));
-        cli_resource_check(out, rsc);
+        cli_resource_check(out, rsc, NULL);
         g_list_free(hosts);
     }
 
diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index 47653a060..68e899c45 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -915,7 +915,7 @@ check_locked(resource_checks_t *checks)
 }
 
 int
-cli_resource_check(pcmk__output_t *out, pe_resource_t *rsc)
+cli_resource_check(pcmk__output_t *out, pe_resource_t *rsc, pe_node_t *node)
 {
     resource_checks_t checks = { .rsc = rsc };
 
-- 
2.31.1


From c3bfde0536f2eb51c81bf34fa957c38dc88f9cc3 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 23 Jun 2022 09:49:03 -0500
Subject: [PATCH 08/14] Feature: tools: crm_resource --why now checks node
 health status

Closes T65
---
 tools/crm_resource.h         |  1 +
 tools/crm_resource_print.c   | 13 +++++++++
 tools/crm_resource_runtime.c | 56 ++++++++++++++++++++++++++++++++++++
 3 files changed, 70 insertions(+)

diff --git a/tools/crm_resource.h b/tools/crm_resource.h
index f7e44476d..ae4b02a98 100644
--- a/tools/crm_resource.h
+++ b/tools/crm_resource.h
@@ -37,6 +37,7 @@ enum resource_check_flags {
     rsc_unpromotable    = (1 << 1),
     rsc_unmanaged       = (1 << 2),
     rsc_locked          = (1 << 3),
+    rsc_node_health     = (1 << 4),
 };
 
 typedef struct resource_checks_s {
diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
index 087819601..27fd76aaf 100644
--- a/tools/crm_resource_print.c
+++ b/tools/crm_resource_print.c
@@ -477,6 +477,15 @@ resource_check_list_default(pcmk__output_t *out, va_list args) {
                        parent->id, checks->lock_node);
     }
 
+    if (pcmk_is_set(checks->flags, rsc_node_health)) {
+        out->list_item(out, "check",
+                       "'%s' cannot run on unhealthy nodes due to "
+                       PCMK__OPT_NODE_HEALTH_STRATEGY "='%s'",
+                       parent->id,
+                       pe_pref(checks->rsc->cluster->config_hash,
+                               PCMK__OPT_NODE_HEALTH_STRATEGY));
+    }
+
     out->end_list(out);
     return pcmk_rc_ok;
 }
@@ -508,6 +517,10 @@ resource_check_list_xml(pcmk__output_t *out, va_list args) {
         crm_xml_add(node, "locked-to", checks->lock_node);
     }
 
+    if (pcmk_is_set(checks->flags, rsc_node_health)) {
+        pcmk__xe_set_bool_attr(node, "unhealthy", true);
+    }
+
     return pcmk_rc_ok;
 }
 
diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index 68e899c45..2aa3efe38 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -914,6 +914,61 @@ check_locked(resource_checks_t *checks)
     }
 }
 
+static bool
+node_is_unhealthy(pe_node_t *node)
+{
+    switch (pe__health_strategy(node->details->data_set)) {
+        case pcmk__health_strategy_none:
+            break;
+
+        case pcmk__health_strategy_no_red:
+            if (pe__node_health(node) < 0) {
+                return true;
+            }
+            break;
+
+        case pcmk__health_strategy_only_green:
+            if (pe__node_health(node) <= 0) {
+                return true;
+            }
+            break;
+
+        case pcmk__health_strategy_progressive:
+        case pcmk__health_strategy_custom:
+            /* @TODO These are finite scores, possibly with rules, and possibly
+             * combining with other scores, so attributing these as a cause is
+             * nontrivial.
+             */
+            break;
+    }
+    return false;
+}
+
+static void
+check_node_health(resource_checks_t *checks, pe_node_t *node)
+{
+    if (node == NULL) {
+        GHashTableIter iter;
+        bool allowed = false;
+        bool all_nodes_unhealthy = true;
+
+        g_hash_table_iter_init(&iter, checks->rsc->allowed_nodes);
+        while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
+            allowed = true;
+            if (!node_is_unhealthy(node)) {
+                all_nodes_unhealthy = false;
+                break;
+            }
+        }
+        if (allowed && all_nodes_unhealthy) {
+            checks->flags |= rsc_node_health;
+        }
+
+    } else if (node_is_unhealthy(node)) {
+        checks->flags |= rsc_node_health;
+    }
+}
+
 int
 cli_resource_check(pcmk__output_t *out, pe_resource_t *rsc, pe_node_t *node)
 {
@@ -922,6 +977,7 @@ cli_resource_check(pcmk__output_t *out, pe_resource_t *rsc, pe_node_t *node)
     check_role(&checks);
     check_managed(&checks);
     check_locked(&checks);
+    check_node_health(&checks, node);
 
     return out->message(out, "resource-check-list", &checks);
 }
-- 
2.31.1


From 48730fd51a22e109514764a039e5c89fd204ad4c Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 23 Jun 2022 10:41:48 -0500
Subject: [PATCH 09/14] Low: schemas: copy crm_resource API schema in
 preparation for changes

---
 include/crm/common/output_internal.h |   2 +-
 xml/api/crm_resource-2.22.rng        | 303 +++++++++++++++++++++++++++
 2 files changed, 304 insertions(+), 1 deletion(-)
 create mode 100644 xml/api/crm_resource-2.22.rng

diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
index ca16227fe..bdcae8ad6 100644
--- a/include/crm/common/output_internal.h
+++ b/include/crm/common/output_internal.h
@@ -28,7 +28,7 @@ extern "C" {
  */
 
 
-#  define PCMK__API_VERSION "2.21"
+#  define PCMK__API_VERSION "2.22"
 
 #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
 #  define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
diff --git a/xml/api/crm_resource-2.22.rng b/xml/api/crm_resource-2.22.rng
new file mode 100644
index 000000000..cd74da0d8
--- /dev/null
+++ b/xml/api/crm_resource-2.22.rng
@@ -0,0 +1,303 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+    <start>
+        <ref name="element-crm-resource"/>
+    </start>
+
+    <define name="element-crm-resource">
+        <choice>
+            <ref name="agents-list" />
+            <ref name="alternatives-list" />
+            <ref name="constraints-list" />
+            <externalRef href="generic-list-2.4.rng"/>
+            <element name="metadata"> <text/> </element>
+            <ref name="locate-list" />
+            <ref name="operations-list" />
+            <ref name="providers-list" />
+            <ref name="reasons-list" />
+            <ref name="resource-check" />
+            <ref name="resource-config" />
+            <ref name="resources-list" />
+            <ref name="resource-agent-action" />
+        </choice>
+    </define>
+
+    <define name="agents-list">
+        <element name="agents">
+            <attribute name="standard"> <text/> </attribute>
+            <optional>
+                <attribute name="provider"> <text/> </attribute>
+            </optional>
+            <zeroOrMore>
+                <element name="agent"> <text/> </element>
+            </zeroOrMore>
+        </element>
+    </define>
+
+    <define name="alternatives-list">
+        <element name="providers">
+            <attribute name="for"> <text/> </attribute>
+            <zeroOrMore>
+                <element name="provider"> <text/> </element>
+            </zeroOrMore>
+        </element>
+    </define>
+
+    <define name="constraints-list">
+        <element name="constraints">
+            <interleave>
+                <zeroOrMore>
+                    <ref name="rsc-location" />
+                </zeroOrMore>
+                <zeroOrMore>
+                    <ref name="rsc-colocation" />
+                </zeroOrMore>
+            </interleave>
+        </element>
+    </define>
+
+    <define name="locate-list">
+        <element name="nodes">
+            <attribute name="resource"> <text/> </attribute>
+            <zeroOrMore>
+                <element name="node">
+                    <optional>
+                        <attribute name="state"><value>promoted</value></attribute>
+                    </optional>
+                    <text/>
+                </element>
+            </zeroOrMore>
+        </element>
+    </define>
+
+    <define name="rsc-location">
+        <element name="rsc_location">
+            <attribute name="node"> <text/> </attribute>
+            <attribute name="rsc"> <text/> </attribute>
+            <attribute name="id"> <text/> </attribute>
+            <externalRef href="../score.rng"/>
+        </element>
+    </define>
+
+    <define name="operations-list">
+        <element name="operations">
+            <oneOrMore>
+                <ref name="element-operation-list" />
+            </oneOrMore>
+        </element>
+    </define>
+
+    <define name="providers-list">
+        <element name="providers">
+            <attribute name="standard"> <value>ocf</value> </attribute>
+            <optional>
+                <attribute name="agent"> <text/> </attribute>
+            </optional>
+            <zeroOrMore>
+                <element name="provider"> <text/> </element>
+            </zeroOrMore>
+        </element>
+    </define>
+
+    <define name="reasons-list">
+        <choice>
+            <ref name="no-resource-or-uname"/>
+            <ref name="resource-and-uname"/>
+            <ref name="no-resource-but-uname"/>
+            <ref name="resource-but-no-uname"/>
+        </choice>
+    </define>
+
+    <define name="no-resource-or-uname">
+        <element name="reason">
+            <element name="resources">
+                <zeroOrMore>
+                    <element name="resource">
+                        <attribute name="id"> <text/> </attribute>
+                        <attribute name="running"> <data type="boolean"/> </attribute>
+                        <ref name="resource-check"/>
+                    </element>
+                </zeroOrMore>
+            </element>
+        </element>
+    </define>
+
+    <define name="resource-and-uname">
+        <element name="reason">
+            <attribute name="running_on"> <text/> </attribute>
+            <ref name="resource-check"/>
+        </element>
+    </define>
+
+    <define name="no-resource-but-uname">
+        <element name="reason">
+            <element name="resources">
+                <zeroOrMore>
+                    <element name="resource">
+                        <attribute name="id"> <text/> </attribute>
+                        <attribute name="running"> <data type="boolean"/> </attribute>
+                        <attribute name="host"> <text/> </attribute>
+                        <ref name="resource-check"/>
+                    </element>
+                </zeroOrMore>
+            </element>
+        </element>
+    </define>
+
+    <define name="resource-but-no-uname">
+        <element name="reason">
+            <attribute name="running"> <data type="boolean"/> </attribute>
+            <ref name="resource-check"/>
+        </element>
+    </define>
+
+    <define name="resource-config">
+        <element name="resource_config">
+            <externalRef href="resources-2.4.rng" />
+            <element name="xml"> <text/> </element>
+        </element>
+    </define>
+
+    <define name="resource-check">
+        <element name="check">
+            <attribute name="id"> <text/> </attribute>
+            <optional>
+                <choice>
+                    <attribute name="remain_stopped"><value>true</value></attribute>
+                    <attribute name="promotable"><value>false</value></attribute>
+                </choice>
+            </optional>
+            <optional>
+                <attribute name="unmanaged"><value>true</value></attribute>
+            </optional>
+            <optional>
+                <attribute name="locked-to"> <text/> </attribute>
+            </optional>
+        </element>
+    </define>
+
+    <define name="resources-list">
+        <element name="resources">
+            <zeroOrMore>
+                <externalRef href="resources-2.4.rng" />
+            </zeroOrMore>
+        </element>
+    </define>
+
+    <define name="rsc-colocation">
+        <element name="rsc_colocation">
+            <attribute name="id"> <text/> </attribute>
+            <attribute name="rsc"> <text/> </attribute>
+            <attribute name="with-rsc"> <text/> </attribute>
+            <externalRef href="../score.rng"/>
+            <optional>
+                <attribute name="node-attribute"> <text/> </attribute>
+            </optional>
+            <optional>
+                <attribute name="rsc-role">
+                    <ref name="attribute-roles"/>
+                </attribute>
+            </optional>
+            <optional>
+                <attribute name="with-rsc-role">
+                    <ref name="attribute-roles"/>
+                </attribute>
+            </optional>
+        </element>
+    </define>
+
+    <define name="element-operation-list">
+        <element name="operation">
+            <optional>
+                <group>
+                    <attribute name="rsc"> <text/> </attribute>
+                    <attribute name="agent"> <text/> </attribute>
+                </group>
+            </optional>
+            <attribute name="op"> <text/> </attribute>
+            <attribute name="node"> <text/> </attribute>
+            <attribute name="call"> <data type="integer" /> </attribute>
+            <attribute name="rc"> <data type="nonNegativeInteger" /> </attribute>
+            <optional>
+                <attribute name="last-rc-change"> <text/> </attribute>
+                <attribute name="exec-time"> <data type="nonNegativeInteger" /> </attribute>
+            </optional>
+            <attribute name="status"> <text/> </attribute>
+        </element>
+    </define>
+
+    <define name="resource-agent-action">
+        <element name="resource-agent-action">
+            <attribute name="action"> <text/> </attribute>
+            <optional>
+                <attribute name="rsc"> <text/> </attribute>
+            </optional>
+            <attribute name="class"> <text/> </attribute>
+            <attribute name="type"> <text/> </attribute>
+            <optional>
+                <attribute name="provider"> <text/> </attribute>
+            </optional>
+            <optional>
+                <ref name="overrides-list"/>
+            </optional>
+            <ref name="agent-status"/>
+            <optional>
+                <choice>
+                    <element name="command">
+                        <text />
+                    </element>
+                    <externalRef href="command-output-1.0.rng"/>
+                </choice>
+            </optional>
+        </element>
+    </define>
+
+    <define name="overrides-list">
+        <element name="overrides">
+            <zeroOrMore>
+                <element name="override">
+                    <optional>
+                        <attribute name="rsc"> <text/> </attribute>
+                    </optional>
+                    <attribute name="name"> <text/> </attribute>
+                    <attribute name="value"> <text/> </attribute>
+                </element>
+            </zeroOrMore>
+        </element>
+    </define>
+
+    <define name="agent-status">
+        <element name="agent-status">
+            <attribute name="code"> <data type="integer" /> </attribute>
+            <optional>
+                <attribute name="message"> <text/> </attribute>
+            </optional>
+            <optional>
+                <attribute name="execution_code"> <data type="integer" /> </attribute>
+            </optional>
+            <optional>
+                <attribute name="execution_message"> <text/> </attribute>
+            </optional>
+            <optional>
+                <attribute name="reason"> <text/> </attribute>
+            </optional>
+        </element>
+    </define>
+
+    <define name="attribute-roles">
+        <choice>
+            <value>Stopped</value>
+            <value>Started</value>
+            <value>Promoted</value>
+            <value>Unpromoted</value>
+
+            <!-- These synonyms for Promoted/Unpromoted are allowed for
+                 backward compatibility with output from older Pacemaker
+                 versions that used them -->
+            <value>Master</value>
+            <value>Slave</value>
+        </choice>
+    </define>
+</grammar>
-- 
2.31.1


From 75a885d9da92c84038e3abf732c11cf3fb6a79a7 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 23 Jun 2022 11:33:50 -0500
Subject: [PATCH 10/14] Fix: tools: correct crm_resource --why schema to match
 actual output

If both a resource and node name are specified, "running_on" is optional
---
 xml/api/crm_resource-2.22.rng | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/xml/api/crm_resource-2.22.rng b/xml/api/crm_resource-2.22.rng
index cd74da0d8..e89d850da 100644
--- a/xml/api/crm_resource-2.22.rng
+++ b/xml/api/crm_resource-2.22.rng
@@ -126,7 +126,9 @@
 
     <define name="resource-and-uname">
         <element name="reason">
-            <attribute name="running_on"> <text/> </attribute>
+            <optional>
+                <attribute name="running_on"> <text/> </attribute>
+            </optional>
             <ref name="resource-check"/>
         </element>
     </define>
-- 
2.31.1


From 5e4f993859dd68a3f88cb0648ace7b3837316288 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 23 Jun 2022 11:20:03 -0500
Subject: [PATCH 11/14] Low: schemas: simplify crm_resource --why schema

---
 xml/api/crm_resource-2.22.rng | 64 ++++++++++++-----------------------
 1 file changed, 22 insertions(+), 42 deletions(-)

diff --git a/xml/api/crm_resource-2.22.rng b/xml/api/crm_resource-2.22.rng
index e89d850da..2d2ba839f 100644
--- a/xml/api/crm_resource-2.22.rng
+++ b/xml/api/crm_resource-2.22.rng
@@ -102,56 +102,36 @@
     </define>
 
     <define name="reasons-list">
-        <choice>
-            <ref name="no-resource-or-uname"/>
-            <ref name="resource-and-uname"/>
-            <ref name="no-resource-but-uname"/>
-            <ref name="resource-but-no-uname"/>
-        </choice>
-    </define>
-
-    <define name="no-resource-or-uname">
-        <element name="reason">
-            <element name="resources">
-                <zeroOrMore>
-                    <element name="resource">
-                        <attribute name="id"> <text/> </attribute>
-                        <attribute name="running"> <data type="boolean"/> </attribute>
-                        <ref name="resource-check"/>
-                    </element>
-                </zeroOrMore>
-            </element>
-        </element>
-    </define>
-
-    <define name="resource-and-uname">
         <element name="reason">
+            <!-- set only when resource and node are both specified -->
             <optional>
                 <attribute name="running_on"> <text/> </attribute>
             </optional>
-            <ref name="resource-check"/>
-        </element>
-    </define>
 
-    <define name="no-resource-but-uname">
-        <element name="reason">
-            <element name="resources">
-                <zeroOrMore>
-                    <element name="resource">
-                        <attribute name="id"> <text/> </attribute>
-                        <attribute name="running"> <data type="boolean"/> </attribute>
-                        <attribute name="host"> <text/> </attribute>
-                        <ref name="resource-check"/>
-                    </element>
-                </zeroOrMore>
-            </element>
+            <!-- set only when only a resource is specified -->
+            <optional>
+                <attribute name="running"> <data type="boolean"/> </attribute>
+            </optional>
+
+            <choice>
+                <ref name="reasons-with-no-resource"/>
+                <ref name="resource-check"/>
+            </choice>
         </element>
     </define>
 
-    <define name="resource-but-no-uname">
-        <element name="reason">
-            <attribute name="running"> <data type="boolean"/> </attribute>
-            <ref name="resource-check"/>
+    <define name="reasons-with-no-resource">
+        <element name="resources">
+            <zeroOrMore>
+                <element name="resource">
+                    <attribute name="id"> <text/> </attribute>
+                    <attribute name="running"> <data type="boolean"/> </attribute>
+                    <optional>
+                        <attribute name="host"> <text/> </attribute>
+                    </optional>
+                    <ref name="resource-check"/>
+                </element>
+            </zeroOrMore>
         </element>
     </define>
 
-- 
2.31.1


From 79bdbbde27ad340c2054089aaecf5e0b49296e59 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 23 Jun 2022 11:28:11 -0500
Subject: [PATCH 12/14] Test: cts-cli: use validated XML output for
 crm_resource --why test

---
 cts/cli/regression.tools.exp | 8 ++++++--
 cts/cts-cli.in               | 4 ++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/cts/cli/regression.tools.exp b/cts/cli/regression.tools.exp
index 0d1cfa2ab..4237a3ec5 100644
--- a/cts/cli/regression.tools.exp
+++ b/cts/cli/regression.tools.exp
@@ -888,8 +888,12 @@ Deleted 'dummy' option: id=dummy-meta_attributes-is-managed name=is-managed
 =#=#=#= End test: Create another resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - Create another resource meta attribute
 =#=#=#= Begin test: Show why a resource is not running =#=#=#=
-Resource dummy is not running
-Configuration specifies 'dummy' should remain stopped
+<pacemaker-result api-version="X" request="crm_resource -Y -r dummy --output-as=xml">
+  <reason running="false">
+    <check id="dummy" remain_stopped="true"/>
+  </reason>
+  <status code="0" message="OK"/>
+</pacemaker-result>
 =#=#=#= End test: Show why a resource is not running - OK (0) =#=#=#=
 * Passed: crm_resource   - Show why a resource is not running
 =#=#=#= Begin test: Remove another resource meta attribute =#=#=#=
diff --git a/cts/cts-cli.in b/cts/cts-cli.in
index 8565c485a..289ac966f 100755
--- a/cts/cts-cli.in
+++ b/cts/cts-cli.in
@@ -657,8 +657,8 @@ function test_tools() {
     test_assert_validate $CRM_EX_OK 0
 
     desc="Show why a resource is not running"
-    cmd="crm_resource -Y -r dummy"
-    test_assert $CRM_EX_OK 0
+    cmd="crm_resource -Y -r dummy --output-as=xml"
+    test_assert_validate $CRM_EX_OK 0
 
     desc="Remove another resource meta attribute"
     cmd="crm_resource -r dummy --meta -d target-role --output-as=xml"
-- 
2.31.1


From 929d1b40e82f186e7e31e380db2620e7e23968f1 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 23 Jun 2022 10:43:22 -0500
Subject: [PATCH 13/14] Low: schemas: update crm_resource --why schema for new
 health check

---
 xml/api/crm_resource-2.22.rng | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/xml/api/crm_resource-2.22.rng b/xml/api/crm_resource-2.22.rng
index 2d2ba839f..8a4667559 100644
--- a/xml/api/crm_resource-2.22.rng
+++ b/xml/api/crm_resource-2.22.rng
@@ -157,6 +157,9 @@
             <optional>
                 <attribute name="locked-to"> <text/> </attribute>
             </optional>
+            <optional>
+                <attribute name="unhealthy"><value>true</value></attribute>
+            </optional>
         </element>
     </define>
 
-- 
2.31.1


From 6630e55abc7b26be294ab6d42f12cdb7e2c69b55 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 23 Jun 2022 11:07:20 -0500
Subject: [PATCH 14/14] Test: cts-cli: add tests for checking resource status
 on unhealthy node

---
 cts/cli/regression.tools.exp | 112 ++++++++++++++++++++++++++++++++++-
 cts/cts-cli.in               |  12 ++++
 2 files changed, 122 insertions(+), 2 deletions(-)

diff --git a/cts/cli/regression.tools.exp b/cts/cli/regression.tools.exp
index 4237a3ec5..89ae4e97d 100644
--- a/cts/cli/regression.tools.exp
+++ b/cts/cli/regression.tools.exp
@@ -3406,13 +3406,14 @@ Removing constraint: cli-prefer-dummy
 </cib>
 =#=#=#= End test: Clear all implicit constraints for dummy - OK (0) =#=#=#=
 * Passed: crm_resource   - Clear all implicit constraints for dummy
-=#=#=#= Begin test: Delete a resource =#=#=#=
-=#=#=#= Current cib after: Delete a resource =#=#=#=
+=#=#=#= Begin test: Set a node health strategy =#=#=#=
+=#=#=#= Current cib after: Set a node health strategy =#=#=#=
 <cib epoch="55" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+        <nvpair id="cib-bootstrap-options-node-health-strategy" name="node-health-strategy" value="migrate-on-red"/>
       </cluster_property_set>
       <cluster_property_set id="duplicate">
         <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
@@ -3427,6 +3428,113 @@ Removing constraint: cli-prefer-dummy
       <node id="node2" uname="node2"/>
       <node id="node3" uname="node3"/>
     </nodes>
+    <resources>
+      <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+        <meta_attributes id="dummy-meta_attributes"/>
+        <instance_attributes id="dummy-instance_attributes">
+          <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
+        </instance_attributes>
+      </primitive>
+      <primitive id="Fence" class="stonith" type="fence_true"/>
+      <clone id="test-clone">
+        <primitive id="test-primitive" class="ocf" provider="pacemaker" type="Dummy">
+          <meta_attributes id="test-primitive-meta_attributes"/>
+        </primitive>
+        <meta_attributes id="test-clone-meta_attributes">
+          <nvpair id="test-clone-meta_attributes-is-managed" name="is-managed" value="true"/>
+        </meta_attributes>
+      </clone>
+    </resources>
+    <constraints/>
+  </configuration>
+  <status/>
+</cib>
+=#=#=#= End test: Set a node health strategy - OK (0) =#=#=#=
+* Passed: crm_attribute  - Set a node health strategy
+=#=#=#= Begin test: Set a node health attribute =#=#=#=
+=#=#=#= Current cib after: Set a node health attribute =#=#=#=
+<cib epoch="56" num_updates="0" admin_epoch="0">
+  <configuration>
+    <crm_config>
+      <cluster_property_set id="cib-bootstrap-options">
+        <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+        <nvpair id="cib-bootstrap-options-node-health-strategy" name="node-health-strategy" value="migrate-on-red"/>
+      </cluster_property_set>
+      <cluster_property_set id="duplicate">
+        <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
+      </cluster_property_set>
+    </crm_config>
+    <nodes>
+      <node id="node1" uname="node1">
+        <instance_attributes id="nodes-node1">
+          <nvpair id="nodes-node1-ram" name="ram" value="1024M"/>
+        </instance_attributes>
+      </node>
+      <node id="node2" uname="node2"/>
+      <node id="node3" uname="node3">
+        <instance_attributes id="nodes-node3">
+          <nvpair id="nodes-node3-.health-cts-cli" name="#health-cts-cli" value="red"/>
+        </instance_attributes>
+      </node>
+    </nodes>
+    <resources>
+      <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+        <meta_attributes id="dummy-meta_attributes"/>
+        <instance_attributes id="dummy-instance_attributes">
+          <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
+        </instance_attributes>
+      </primitive>
+      <primitive id="Fence" class="stonith" type="fence_true"/>
+      <clone id="test-clone">
+        <primitive id="test-primitive" class="ocf" provider="pacemaker" type="Dummy">
+          <meta_attributes id="test-primitive-meta_attributes"/>
+        </primitive>
+        <meta_attributes id="test-clone-meta_attributes">
+          <nvpair id="test-clone-meta_attributes-is-managed" name="is-managed" value="true"/>
+        </meta_attributes>
+      </clone>
+    </resources>
+    <constraints/>
+  </configuration>
+  <status/>
+</cib>
+=#=#=#= End test: Set a node health attribute - OK (0) =#=#=#=
+* Passed: crm_attribute  - Set a node health attribute
+=#=#=#= Begin test: Show why a resource is not running on an unhealthy node =#=#=#=
+<pacemaker-result api-version="X" request="crm_resource -N node3 -Y -r dummy --output-as=xml">
+  <reason>
+    <check id="dummy" unhealthy="true"/>
+  </reason>
+  <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: Show why a resource is not running on an unhealthy node - OK (0) =#=#=#=
+* Passed: crm_resource   - Show why a resource is not running on an unhealthy node
+=#=#=#= Begin test: Delete a resource =#=#=#=
+=#=#=#= Current cib after: Delete a resource =#=#=#=
+<cib epoch="57" num_updates="0" admin_epoch="0">
+  <configuration>
+    <crm_config>
+      <cluster_property_set id="cib-bootstrap-options">
+        <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+        <nvpair id="cib-bootstrap-options-node-health-strategy" name="node-health-strategy" value="migrate-on-red"/>
+      </cluster_property_set>
+      <cluster_property_set id="duplicate">
+        <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
+      </cluster_property_set>
+    </crm_config>
+    <nodes>
+      <node id="node1" uname="node1">
+        <instance_attributes id="nodes-node1">
+          <nvpair id="nodes-node1-ram" name="ram" value="1024M"/>
+        </instance_attributes>
+      </node>
+      <node id="node2" uname="node2"/>
+      <node id="node3" uname="node3">
+        <instance_attributes id="nodes-node3">
+          <nvpair id="nodes-node3-.health-cts-cli" name="#health-cts-cli" value="red"/>
+        </instance_attributes>
+      </node>
+    </nodes>
     <resources>
       <primitive id="Fence" class="stonith" type="fence_true"/>
       <clone id="test-clone">
diff --git a/cts/cts-cli.in b/cts/cts-cli.in
index 289ac966f..990d37cf7 100755
--- a/cts/cts-cli.in
+++ b/cts/cts-cli.in
@@ -883,6 +883,18 @@ function test_tools() {
     cmd="crm_resource -r dummy -U"
     test_assert $CRM_EX_OK
 
+    desc="Set a node health strategy"
+    cmd="crm_attribute -n node-health-strategy -v migrate-on-red"
+    test_assert $CRM_EX_OK
+
+    desc="Set a node health attribute"
+    cmd="crm_attribute -N node3 -n '#health-cts-cli' -v red"
+    test_assert $CRM_EX_OK
+
+    desc="Show why a resource is not running on an unhealthy node"
+    cmd="crm_resource -N node3 -Y -r dummy --output-as=xml"
+    test_assert_validate $CRM_EX_OK 0
+
     desc="Delete a resource"
     cmd="crm_resource -D -r dummy -t primitive"
     test_assert $CRM_EX_OK
-- 
2.31.1