Blob Blame History Raw
From a32b6e14ba51fefbda2d4a699cf1c48dd3a1bb5a Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Thu, 7 Jan 2021 11:37:51 -0500
Subject: [PATCH 01/10] Fix: tools: Don't pass stonith history to
 print_simple_status.

It's not being used.
---
 tools/crm_mon.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 4555516..729f6a1 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2020 the Pacemaker project contributors
+ * Copyright 2004-2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
@@ -1451,14 +1451,13 @@ main(int argc, char **argv)
  * \brief Print one-line status suitable for use with monitoring software
  *
  * \param[in] data_set  Working set of CIB state
- * \param[in] history   List of stonith actions
  *
  * \note This function's output (and the return code when the program exits)
  *       should conform to https://www.monitoring-plugins.org/doc/guidelines.html
  */
 static void
 print_simple_status(pcmk__output_t *out, pe_working_set_t * data_set,
-                    stonith_history_t *history, unsigned int mon_ops)
+                    unsigned int mon_ops)
 {
     GListPtr gIter = NULL;
     int nodes_online = 0;
@@ -2012,7 +2011,7 @@ mon_refresh_display(gpointer user_data)
             break;
 
         case mon_output_monitor:
-            print_simple_status(out, mon_data_set, stonith_history, options.mon_ops);
+            print_simple_status(out, mon_data_set, options.mon_ops);
             if (pcmk_is_set(options.mon_ops, mon_op_has_warnings)) {
                 clean_up(MON_STATUS_WARN);
                 return FALSE;
-- 
1.8.3.1


From 8b9c47089c70295bc0529671ba5991c6d831e14b Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Thu, 7 Jan 2021 13:17:04 -0500
Subject: [PATCH 02/10] Refactor: tools: Don't pass output_format to
 mon_refresh_display.

output_format is a global variable.
---
 tools/crm_mon.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 729f6a1..b801560 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -827,7 +827,7 @@ cib_connect(gboolean full)
 
         rc = cib->cmds->query(cib, NULL, &current_cib, cib_scope_local | cib_sync_call);
         if (rc == pcmk_ok) {
-            mon_refresh_display(&output_format);
+            mon_refresh_display(NULL);
         }
 
         if (rc == pcmk_ok && full) {
-- 
1.8.3.1


From a1b14ad96f12746167da8588dc086b20e6f6d1d6 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Thu, 7 Jan 2021 13:20:14 -0500
Subject: [PATCH 03/10] Refactor: tools: Remove unnecessary checks for cib !=
 NULL.

cib is guaranteed to not be NULL at these points, so there's no need to
do an additional check.  This code was leftover from a previous
reorganization that changed when the cib variable gets initialized.
---
 tools/crm_mon.c | 41 +++++++++++++++++++----------------------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index b801560..1eedd38 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -1346,7 +1346,7 @@ main(int argc, char **argv)
 
     /* Extra sanity checks when in CGI mode */
     if (output_format == mon_output_cgi) {
-        if (cib && cib->variant == cib_file) {
+        if (cib->variant == cib_file) {
             g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, "CGI mode used with CIB file");
             return clean_up(CRM_EX_USAGE);
         } else if (options.external_agent != NULL) {
@@ -1370,33 +1370,30 @@ main(int argc, char **argv)
 
     crm_info("Starting %s", crm_system_name);
 
-    if (cib) {
-
-        do {
-            if (!pcmk_is_set(options.mon_ops, mon_op_one_shot)) {
-                print_as(output_format ,"Waiting until cluster is available on this node ...\n");
-            }
-            rc = cib_connect(!pcmk_is_set(options.mon_ops, mon_op_one_shot));
+    do {
+        if (!pcmk_is_set(options.mon_ops, mon_op_one_shot)) {
+            print_as(output_format ,"Waiting until cluster is available on this node ...\n");
+        }
+        rc = cib_connect(!pcmk_is_set(options.mon_ops, mon_op_one_shot));
 
-            if (pcmk_is_set(options.mon_ops, mon_op_one_shot)) {
-                break;
+        if (pcmk_is_set(options.mon_ops, mon_op_one_shot)) {
+            break;
 
-            } else if (rc != pcmk_ok) {
-                sleep(options.reconnect_msec / 1000);
+        } else if (rc != pcmk_ok) {
+            sleep(options.reconnect_msec / 1000);
 #if CURSES_ENABLED
-                if (output_format == mon_output_console) {
-                    clear();
-                    refresh();
-                }
+            if (output_format == mon_output_console) {
+                clear();
+                refresh();
+            }
 #endif
-            } else {
-                if (output_format == mon_output_html && out->dest != stdout) {
-                    printf("Writing html to %s ...\n", args->output_dest);
-                }
+        } else {
+            if (output_format == mon_output_html && out->dest != stdout) {
+                printf("Writing html to %s ...\n", args->output_dest);
             }
+        }
 
-        } while (rc == -ENOTCONN);
-    }
+    } while (rc == -ENOTCONN);
 
     if (rc != pcmk_ok) {
         if (output_format == mon_output_monitor) {
-- 
1.8.3.1


From fe5284a12765e775905bdfe58711c5733a063132 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Thu, 7 Jan 2021 14:15:40 -0500
Subject: [PATCH 04/10] Fix: tools: mon_refresh_display should return an int.

While GSourceFunc is defined as returning a boolean, our public API
mainloop function expect the dispatch function to return an int.  So
change mon_refresh_display to do so.
---
 tools/crm_mon.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 1eedd38..8657a89 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -125,7 +125,7 @@ struct {
 static void clean_up_connections(void);
 static crm_exit_t clean_up(crm_exit_t exit_code);
 static void crm_diff_update(const char *event, xmlNode * msg);
-static gboolean mon_refresh_display(gpointer user_data);
+static int mon_refresh_display(gpointer user_data);
 static int cib_connect(gboolean full);
 static void mon_st_callback_event(stonith_t * st, stonith_event_t * e);
 static void mon_st_callback_display(stonith_t * st, stonith_event_t * e);
@@ -1925,7 +1925,7 @@ crm_diff_update(const char *event, xmlNode * msg)
     kick_refresh(cib_updated);
 }
 
-static gboolean
+static int
 mon_refresh_display(gpointer user_data)
 {
     xmlNode *cib_copy = copy_xml(current_cib);
@@ -1940,7 +1940,7 @@ mon_refresh_display(gpointer user_data)
         }
         out->err(out, "Upgrade failed: %s", pcmk_strerror(-pcmk_err_schema_validation));
         clean_up(CRM_EX_CONFIG);
-        return FALSE;
+        return 0;
     }
 
     /* get the stonith-history if there is evidence we need it
@@ -1966,7 +1966,7 @@ mon_refresh_display(gpointer user_data)
         }
         free_xml(cib_copy);
         out->err(out, "Reading stonith-history failed");
-        return FALSE;
+        return 0;
     }
 
     if (mon_data_set == NULL) {
@@ -1995,7 +1995,7 @@ mon_refresh_display(gpointer user_data)
                                   options.only_node, options.only_rsc) != 0) {
                 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_CANTCREAT, "Critical: Unable to output html file");
                 clean_up(CRM_EX_CANTCREAT);
-                return FALSE;
+                return 0;
             }
             break;
 
@@ -2044,7 +2044,7 @@ mon_refresh_display(gpointer user_data)
     stonith_history_free(stonith_history);
     stonith_history = NULL;
     pe_reset_working_set(mon_data_set);
-    return TRUE;
+    return 1;
 }
 
 static void
-- 
1.8.3.1


From 7f88a5a428ed73fb5161096ece2517abe1119f06 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Thu, 7 Jan 2021 16:59:02 -0500
Subject: [PATCH 05/10] Refactor: tools: Change a conditional in cib_connect.

This allows unindenting everything that occurs inside that conditional,
which I think makes it a little bit easier to understand what is going
on.
---
 tools/crm_mon.c | 86 +++++++++++++++++++++++++++++----------------------------
 1 file changed, 44 insertions(+), 42 deletions(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 8657a89..b8ba56b 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -804,55 +804,57 @@ cib_connect(gboolean full)
         }
     }
 
-    if (cib->state != cib_connected_query && cib->state != cib_connected_command) {
-        crm_trace("Connecting to the CIB");
-
-        /* Hack: the CIB signon will print the prompt for a password if needed,
-         * but to stderr. If we're in curses, show it on the screen instead.
-         *
-         * @TODO Add a password prompt (maybe including input) function to
-         *       pcmk__output_t and use it in libcib.
-         */
-        if ((output_format == mon_output_console) && need_pass && (cib->variant == cib_remote)) {
-            need_pass = FALSE;
-            print_as(output_format, "Password:");
-        }
+    if (cib->state == cib_connected_query || cib->state == cib_connected_command) {
+        return rc;
+    }
 
-        rc = cib->cmds->signon(cib, crm_system_name, cib_query);
-        if (rc != pcmk_ok) {
-            out->err(out, "Could not connect to the CIB: %s",
-                     pcmk_strerror(rc));
-            return rc;
-        }
+    crm_trace("Connecting to the CIB");
 
-        rc = cib->cmds->query(cib, NULL, &current_cib, cib_scope_local | cib_sync_call);
-        if (rc == pcmk_ok) {
-            mon_refresh_display(NULL);
-        }
+    /* Hack: the CIB signon will print the prompt for a password if needed,
+     * but to stderr. If we're in curses, show it on the screen instead.
+     *
+     * @TODO Add a password prompt (maybe including input) function to
+     *       pcmk__output_t and use it in libcib.
+     */
+    if ((output_format == mon_output_console) && need_pass && (cib->variant == cib_remote)) {
+        need_pass = FALSE;
+        print_as(output_format, "Password:");
+    }
 
-        if (rc == pcmk_ok && full) {
-            if (rc == pcmk_ok) {
-                rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy_regular);
-                if (rc == -EPROTONOSUPPORT) {
-                    print_as
-                        (output_format, "Notification setup not supported, won't be able to reconnect after failure");
-                    if (output_format == mon_output_console) {
-                        sleep(2);
-                    }
-                    rc = pcmk_ok;
-                }
+    rc = cib->cmds->signon(cib, crm_system_name, cib_query);
+    if (rc != pcmk_ok) {
+        out->err(out, "Could not connect to the CIB: %s",
+                 pcmk_strerror(rc));
+        return rc;
+    }
 
-            }
+    rc = cib->cmds->query(cib, NULL, &current_cib, cib_scope_local | cib_sync_call);
+    if (rc == pcmk_ok) {
+        mon_refresh_display(NULL);
+    }
 
-            if (rc == pcmk_ok) {
-                cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
-                rc = cib->cmds->add_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
+    if (rc == pcmk_ok && full) {
+        if (rc == pcmk_ok) {
+            rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy_regular);
+            if (rc == -EPROTONOSUPPORT) {
+                print_as
+                    (output_format, "Notification setup not supported, won't be able to reconnect after failure");
+                if (output_format == mon_output_console) {
+                    sleep(2);
+                }
+                rc = pcmk_ok;
             }
 
-            if (rc != pcmk_ok) {
-                out->err(out, "Notification setup failed, could not monitor CIB actions");
-                clean_up_connections();
-            }
+        }
+
+        if (rc == pcmk_ok) {
+            cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
+            rc = cib->cmds->add_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
+        }
+
+        if (rc != pcmk_ok) {
+            out->err(out, "Notification setup failed, could not monitor CIB actions");
+            clean_up_connections();
         }
     }
     return rc;
-- 
1.8.3.1


From 178ba17e4ee62bef28f8e71cad2c002f823661b5 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Thu, 7 Jan 2021 17:37:36 -0500
Subject: [PATCH 06/10] Refactor: tools: Remove an unnecessary conditional in
 cib_connect.

---
 tools/crm_mon.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index b8ba56b..36249e8 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -834,17 +834,14 @@ cib_connect(gboolean full)
     }
 
     if (rc == pcmk_ok && full) {
-        if (rc == pcmk_ok) {
-            rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy_regular);
-            if (rc == -EPROTONOSUPPORT) {
-                print_as
-                    (output_format, "Notification setup not supported, won't be able to reconnect after failure");
-                if (output_format == mon_output_console) {
-                    sleep(2);
-                }
-                rc = pcmk_ok;
+        rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy_regular);
+        if (rc == -EPROTONOSUPPORT) {
+            print_as
+                (output_format, "Notification setup not supported, won't be able to reconnect after failure");
+            if (output_format == mon_output_console) {
+                sleep(2);
             }
-
+            rc = pcmk_ok;
         }
 
         if (rc == pcmk_ok) {
-- 
1.8.3.1


From 33bac5886417afc5c7bbf56f4d31e0e36f8ae947 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Fri, 8 Jan 2021 10:00:50 -0500
Subject: [PATCH 07/10] Refactor: tools: Simplify another conditional in
 crm_mon.

---
 tools/crm_mon.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 36249e8..8b47bbc 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -1386,10 +1386,8 @@ main(int argc, char **argv)
                 refresh();
             }
 #endif
-        } else {
-            if (output_format == mon_output_html && out->dest != stdout) {
-                printf("Writing html to %s ...\n", args->output_dest);
-            }
+        } else if (output_format == mon_output_html && out->dest != stdout) {
+            printf("Writing html to %s ...\n", args->output_dest);
         }
 
     } while (rc == -ENOTCONN);
-- 
1.8.3.1


From 40bc8b3147e7ebef4318211fa69973a8b5d32e79 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Fri, 8 Jan 2021 12:12:37 -0500
Subject: [PATCH 08/10] Refactor: libcrmcommon,tools,daemons: Put common xpath
 code in one place.

---
 configure.ac                                      |  1 +
 daemons/controld/controld_te_callbacks.c          | 26 +++-----------
 include/crm/common/xml_internal.h                 | 14 +++++++-
 lib/common/tests/Makefile.am                      |  2 +-
 lib/common/tests/xpath/Makefile.am                | 29 +++++++++++++++
 lib/common/tests/xpath/pcmk__xpath_node_id_test.c | 43 +++++++++++++++++++++++
 lib/common/xpath.c                                | 34 +++++++++++++++++-
 tools/crm_mon.c                                   | 25 ++-----------
 8 files changed, 127 insertions(+), 47 deletions(-)
 create mode 100644 lib/common/tests/xpath/Makefile.am
 create mode 100644 lib/common/tests/xpath/pcmk__xpath_node_id_test.c

diff --git a/configure.ac b/configure.ac
index 5959116..ce0f1fe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1920,6 +1920,7 @@ AC_CONFIG_FILES(Makefile                                            \
                 lib/common/tests/operations/Makefile                \
                 lib/common/tests/strings/Makefile                   \
                 lib/common/tests/utils/Makefile                     \
+                lib/common/tests/xpath/Makefile                     \
                 lib/cluster/Makefile                                \
                 lib/cib/Makefile                                    \
                 lib/gnu/Makefile                                    \
diff --git a/daemons/controld/controld_te_callbacks.c b/daemons/controld/controld_te_callbacks.c
index 66fc645..4e3e4e6 100644
--- a/daemons/controld/controld_te_callbacks.c
+++ b/daemons/controld/controld_te_callbacks.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2020 the Pacemaker project contributors
+ * Copyright 2004-2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
@@ -276,24 +276,6 @@ process_resource_updates(const char *node, xmlNode *xml, xmlNode *change,
     }
 }
 
-#define NODE_PATT "/lrm[@id="
-static char *get_node_from_xpath(const char *xpath) 
-{
-    char *nodeid = NULL;
-    char *tmp = strstr(xpath, NODE_PATT);
-
-    if(tmp) {
-        tmp += strlen(NODE_PATT);
-        tmp += 1;
-
-        nodeid = strdup(tmp);
-        tmp = strstr(nodeid, "\'");
-        CRM_ASSERT(tmp);
-        tmp[0] = 0;
-    }
-    return nodeid;
-}
-
 static char *extract_node_uuid(const char *xpath) 
 {
     char *mutable_path = strdup(xpath);
@@ -522,19 +504,19 @@ te_update_diff_v2(xmlNode *diff)
             process_resource_updates(ID(match), match, change, op, xpath);
 
         } else if (strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
-            char *local_node = get_node_from_xpath(xpath);
+            char *local_node = pcmk__xpath_node_id(xpath, "lrm");
 
             process_resource_updates(local_node, match, change, op, xpath);
             free(local_node);
 
         } else if (strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
-            char *local_node = get_node_from_xpath(xpath);
+            char *local_node = pcmk__xpath_node_id(xpath, "lrm");
 
             process_lrm_resource_diff(match, local_node);
             free(local_node);
 
         } else if (strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
-            char *local_node = get_node_from_xpath(xpath);
+            char *local_node = pcmk__xpath_node_id(xpath, "lrm");
 
             process_graph_event(match, local_node);
             free(local_node);
diff --git a/include/crm/common/xml_internal.h b/include/crm/common/xml_internal.h
index 1e80bc6..d8694ee 100644
--- a/include/crm/common/xml_internal.h
+++ b/include/crm/common/xml_internal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-2020 the Pacemaker project contributors
+ * Copyright 2017-2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
@@ -273,4 +273,16 @@ pcmk__xe_first_attr(const xmlNode *xe)
     return (xe == NULL)? NULL : xe->properties;
 }
 
+/*!
+ * \internal
+ * \brief Extract the ID attribute from an XML element
+ *
+ * \param[in] xpath String to search
+ * \param[in] node  Node to get the ID for
+ *
+ * \return ID attribute of \p node in xpath string \p xpath
+ */
+char *
+pcmk__xpath_node_id(const char *xpath, const char *node);
+
 #endif // PCMK__XML_INTERNAL__H
diff --git a/lib/common/tests/Makefile.am b/lib/common/tests/Makefile.am
index 2c33cc5..4c6e8b4 100644
--- a/lib/common/tests/Makefile.am
+++ b/lib/common/tests/Makefile.am
@@ -1 +1 @@
-SUBDIRS = agents cmdline flags operations strings utils
+SUBDIRS = agents cmdline flags operations strings utils xpath
diff --git a/lib/common/tests/xpath/Makefile.am b/lib/common/tests/xpath/Makefile.am
new file mode 100644
index 0000000..7a53683
--- /dev/null
+++ b/lib/common/tests/xpath/Makefile.am
@@ -0,0 +1,29 @@
+#
+# Copyright 2021 the Pacemaker project contributors
+#
+# The version control history for this file may have further details.
+#
+# This source code is licensed under the GNU General Public License version 2
+# or later (GPLv2+) WITHOUT ANY WARRANTY.
+#
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+LDADD = $(top_builddir)/lib/common/libcrmcommon.la
+
+include $(top_srcdir)/mk/glib-tap.mk
+
+# Add each test program here.  Each test should be written as a little standalone
+# program using the glib unit testing functions.  See the documentation for more
+# information.
+#
+# https://developer.gnome.org/glib/unstable/glib-Testing.html
+#
+# Add "_test" to the end of all test program names to simplify .gitignore.
+test_programs =	pcmk__xpath_node_id_test
+
+# If any extra data needs to be added to the source distribution, add it to the
+# following list.
+dist_test_data =
+
+# If any extra data needs to be used by tests but should not be added to the
+# source distribution, add it to the following list.
+test_data =
diff --git a/lib/common/tests/xpath/pcmk__xpath_node_id_test.c b/lib/common/tests/xpath/pcmk__xpath_node_id_test.c
new file mode 100644
index 0000000..f6b5c10
--- /dev/null
+++ b/lib/common/tests/xpath/pcmk__xpath_node_id_test.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+#include <crm/common/xml_internal.h>
+
+static void
+empty_input(void) {
+    g_assert_null(pcmk__xpath_node_id(NULL, "lrm"));
+    g_assert_null(pcmk__xpath_node_id("", "lrm"));
+    g_assert_null(pcmk__xpath_node_id("/blah/blah", NULL));
+    g_assert_null(pcmk__xpath_node_id("/blah/blah", ""));
+    g_assert_null(pcmk__xpath_node_id(NULL, NULL));
+}
+
+static void
+not_present(void) {
+    g_assert_null(pcmk__xpath_node_id("/some/xpath/string[@id='xyz']", "lrm"));
+    g_assert_null(pcmk__xpath_node_id("/some/xpath/containing[@id='lrm']", "lrm"));
+}
+
+static void
+present(void) {
+    g_assert_cmpint(strcmp(pcmk__xpath_node_id("/some/xpath/containing/lrm[@id='xyz']", "lrm"), "xyz"), ==, 0);
+    g_assert_cmpint(strcmp(pcmk__xpath_node_id("/some/other/lrm[@id='xyz']/xpath", "lrm"), "xyz"), ==, 0);
+}
+
+int
+main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/common/xpath/node_id/empty_input", empty_input);
+    g_test_add_func("/common/xpath/node_id/not_present", not_present);
+    g_test_add_func("/common/xpath/node_id/present", present);
+    return g_test_run();
+}
diff --git a/lib/common/xpath.c b/lib/common/xpath.c
index 6fa4941..7851a7c 100644
--- a/lib/common/xpath.c
+++ b/lib/common/xpath.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2020 the Pacemaker project contributors
+ * Copyright 2004-2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
@@ -11,6 +11,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <crm/msg_xml.h>
+#include <crm/common/xml_internal.h>
 #include "crmcommon_private.h"
 
 /*
@@ -297,3 +298,34 @@ xml_get_path(xmlNode *xml)
     }
     return NULL;
 }
+
+char *
+pcmk__xpath_node_id(const char *xpath, const char *node)
+{
+    char *retval = NULL;
+    char *patt = NULL;
+    char *start = NULL;
+    char *end = NULL;
+
+    if (node == NULL || xpath == NULL) {
+        return retval;
+    }
+
+    patt = crm_strdup_printf("/%s[@id=", node);
+    start = strstr(xpath, patt);
+
+    if (!start) {
+        free(patt);
+        return retval;
+    }
+
+    start += strlen(patt);
+    start++;
+
+    end = strstr(start, "\'");
+    CRM_ASSERT(end);
+    retval = strndup(start, end-start);
+
+    free(patt);
+    return retval;
+}
diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 8b47bbc..ff1b86b 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -1719,25 +1719,6 @@ mon_trigger_refresh(gpointer user_data)
     return FALSE;
 }
 
-#define NODE_PATT "/lrm[@id="
-static char *
-get_node_from_xpath(const char *xpath)
-{
-    char *nodeid = NULL;
-    char *tmp = strstr(xpath, NODE_PATT);
-
-    if(tmp) {
-        tmp += strlen(NODE_PATT);
-        tmp += 1;
-
-        nodeid = strdup(tmp);
-        tmp = strstr(nodeid, "\'");
-        CRM_ASSERT(tmp);
-        tmp[0] = 0;
-    }
-    return nodeid;
-}
-
 static void
 crm_diff_update_v2(const char *event, xmlNode * msg)
 {
@@ -1822,19 +1803,19 @@ crm_diff_update_v2(const char *event, xmlNode * msg)
             handle_rsc_op(match, node);
 
         } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
-            char *local_node = get_node_from_xpath(xpath);
+            char *local_node = pcmk__xpath_node_id(xpath, "lrm");
 
             handle_rsc_op(match, local_node);
             free(local_node);
 
         } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
-            char *local_node = get_node_from_xpath(xpath);
+            char *local_node = pcmk__xpath_node_id(xpath, "lrm");
 
             handle_rsc_op(match, local_node);
             free(local_node);
 
         } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
-            char *local_node = get_node_from_xpath(xpath);
+            char *local_node = pcmk__xpath_node_id(xpath, "lrm");
 
             handle_rsc_op(match, local_node);
             free(local_node);
-- 
1.8.3.1


From b0126373d8b2a739ec5b985a7e1f530e850618d3 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Mon, 11 Jan 2021 10:20:11 -0500
Subject: [PATCH 09/10] Refactor: libpacemaker: Move reduce_stonith_history
 into the library.

And also rename it to pcmk__reduce_fence_history.  I don't see anywhere
else that could use this function at the moment, but it seems too
generic to keep in crm_mon.
---
 include/pcmki/pcmki_fence.h | 16 +++++++++++++-
 lib/pacemaker/pcmk_fence.c  | 45 ++++++++++++++++++++++++++++++++++++++-
 tools/crm_mon.c             | 52 +--------------------------------------------
 3 files changed, 60 insertions(+), 53 deletions(-)

diff --git a/include/pcmki/pcmki_fence.h b/include/pcmki/pcmki_fence.h
index 241d030..d4cef68 100644
--- a/include/pcmki/pcmki_fence.h
+++ b/include/pcmki/pcmki_fence.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2020 the Pacemaker project contributors
+ * Copyright 2019-2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
@@ -219,4 +219,18 @@ int pcmk__fence_validate(pcmk__output_t *out, stonith_t *st, const char *agent,
                          const char *id, stonith_key_value_t *params,
                          unsigned int timeout);
 
+/**
+ * \brief Reduce the STONITH history
+ *
+ * STONITH history is reduced as follows:
+ *  - The last successful action of every action-type and target is kept
+ *  - For failed actions, who failed is kept
+ *  - All actions in progress are kept
+ *
+ * \param[in] history List of STONITH actions
+ *
+ * \return The reduced history
+ */
+stonith_history_t *
+pcmk__reduce_fence_history(stonith_history_t *history);
 #endif
diff --git a/lib/pacemaker/pcmk_fence.c b/lib/pacemaker/pcmk_fence.c
index d591379..34540cc 100644
--- a/lib/pacemaker/pcmk_fence.c
+++ b/lib/pacemaker/pcmk_fence.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009-2020 the Pacemaker project contributors
+ * Copyright 2009-2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
@@ -520,3 +520,46 @@ pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent,
     return rc;
 }
 #endif
+
+stonith_history_t *
+pcmk__reduce_fence_history(stonith_history_t *history)
+{
+    stonith_history_t *new, *hp, *np;
+
+    if (!history) {
+        return history;
+    }
+
+    new = history;
+    hp = new->next;
+    new->next = NULL;
+
+    while (hp) {
+        stonith_history_t *hp_next = hp->next;
+
+        hp->next = NULL;
+
+        for (np = new; ; np = np->next) {
+            if ((hp->state == st_done) || (hp->state == st_failed)) {
+                /* action not in progress */
+                if (pcmk__str_eq(hp->target, np->target, pcmk__str_casei) &&
+                    pcmk__str_eq(hp->action, np->action, pcmk__str_casei) &&
+                    (hp->state == np->state) &&
+                    ((hp->state == st_done) ||
+                     pcmk__str_eq(hp->delegate, np->delegate, pcmk__str_casei))) {
+                        /* purge older hp */
+                        stonith_history_free(hp);
+                        break;
+                }
+            }
+
+            if (!np->next) {
+                np->next = hp;
+                break;
+            }
+        }
+        hp = hp_next;
+    }
+
+    return new;
+}
diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index ff1b86b..2179f53 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -1520,56 +1520,6 @@ print_simple_status(pcmk__output_t *out, pe_working_set_t * data_set,
     /* coverity[leaked_storage] False positive */
 }
 
-/*!
- * \internal
- * \brief Reduce the stonith-history
- *        for successful actions we keep the last of every action-type & target
- *        for failed actions we record as well who had failed
- *        for actions in progress we keep full track
- *
- * \param[in] history    List of stonith actions
- *
- */
-static stonith_history_t *
-reduce_stonith_history(stonith_history_t *history)
-{
-    stonith_history_t *new = history, *hp, *np;
-
-    if (new) {
-        hp = new->next;
-        new->next = NULL;
-
-        while (hp) {
-            stonith_history_t *hp_next = hp->next;
-
-            hp->next = NULL;
-
-            for (np = new; ; np = np->next) {
-                if ((hp->state == st_done) || (hp->state == st_failed)) {
-                    /* action not in progress */
-                    if (pcmk__str_eq(hp->target, np->target, pcmk__str_casei) &&
-                        pcmk__str_eq(hp->action, np->action, pcmk__str_casei) &&
-                        (hp->state == np->state) &&
-                        ((hp->state == st_done) ||
-                         pcmk__str_eq(hp->delegate, np->delegate, pcmk__str_casei))) {
-                            /* purge older hp */
-                            stonith_history_free(hp);
-                            break;
-                    }
-                }
-
-                if (!np->next) {
-                    np->next = hp;
-                    break;
-                }
-            }
-            hp = hp_next;
-        }
-    }
-
-    return new;
-}
-
 static int
 send_custom_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
                  int status, const char *desc)
@@ -1935,7 +1885,7 @@ mon_refresh_display(gpointer user_data)
                 if (!pcmk_is_set(options.mon_ops, mon_op_fence_full_history)
                     && (output_format != mon_output_xml)) {
 
-                    stonith_history = reduce_stonith_history(stonith_history);
+                    stonith_history = pcmk__reduce_fence_history(stonith_history);
                 }
                 break; /* all other cases are errors */
             }
-- 
1.8.3.1


From af3f1368bc76eb498c2c96b3eda9324b579c9380 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Tue, 12 Jan 2021 15:46:55 -0500
Subject: [PATCH 10/10] Low: tools: Adjust fencing shown indicator in crm_mon.

If any of the various fencing flags are set, but not all of them, no '*'
will be shown next to the fencing line in the interactive change screen.
This makes it seem like fencing should not be shown, and hitting 'm'
should toggle the fencing display on.  However, that's not the case and
hitting 'm' will actually toggle fencing off.  Hitting it again will
toggle it on and the '*' will appear.

This is confusing, so just display the '*' if any fencing flag is set.
---
 tools/crm_mon.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index 2179f53..8ec97bb 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -984,7 +984,10 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer user_dat
         print_option_help(out, 'R', pcmk_is_set(options.mon_ops, mon_op_print_clone_detail));
         print_option_help(out, 'b', pcmk_is_set(options.mon_ops, mon_op_print_brief));
         print_option_help(out, 'j', pcmk_is_set(options.mon_ops, mon_op_print_pending));
-        print_option_help(out, 'm', pcmk_is_set(show, mon_show_fencing_all));
+        print_option_help(out, 'm', pcmk_any_flags_set(show,
+                                                       mon_show_fence_failed
+                                                      |mon_show_fence_pending
+                                                      |mon_show_fence_worked));
         out->info(out, "%s", "\nToggle fields via field letter, type any other key to return");
     }
 
-- 
1.8.3.1