26ad17
From e01a1178fd8f5b99895683b3af9998e9485d12a4 Mon Sep 17 00:00:00 2001
26ad17
From: Ken Gaillot <kgaillot@redhat.com>
26ad17
Date: Fri, 24 Apr 2020 16:42:02 -0500
26ad17
Subject: [PATCH 1/3] Feature: controller: add new IPC API command for getting
26ad17
 node list
26ad17
26ad17
This is based on and will replace the corresponding functionality in
26ad17
pacemakerd.
26ad17
---
26ad17
 daemons/controld/controld_messages.c | 44 ++++++++++++++++++++++++++++--
26ad17
 include/crm/common/ipc_controld.h    | 13 +++++++++
26ad17
 include/crm_internal.h               |  1 +
26ad17
 lib/common/ipc_controld.c            | 53 ++++++++++++++++++++++++++++++++++++
26ad17
 4 files changed, 109 insertions(+), 2 deletions(-)
26ad17
26ad17
diff --git a/daemons/controld/controld_messages.c b/daemons/controld/controld_messages.c
26ad17
index 0be04d0..423f006 100644
26ad17
--- a/daemons/controld/controld_messages.c
26ad17
+++ b/daemons/controld/controld_messages.c
26ad17
@@ -375,10 +375,11 @@ relay_message(xmlNode * msg, gboolean originated_locally)
26ad17
             is_local = 0;
26ad17
 
26ad17
         } else if (is_for_crm) {
26ad17
-            if (safe_str_eq(task, CRM_OP_NODE_INFO)) {
26ad17
+            if (safe_str_eq(task, CRM_OP_NODE_INFO)
26ad17
+                || safe_str_eq(task, PCMK__CONTROLD_CMD_NODES)) {
26ad17
                 /* Node info requests do not specify a host, which is normally
26ad17
                  * treated as "all hosts", because the whole point is that the
26ad17
-                 * client doesn't know the local node name. Always handle these
26ad17
+                 * client may not know the local node name. Always handle these
26ad17
                  * requests locally.
26ad17
                  */
26ad17
                 is_local = 1;
26ad17
@@ -784,6 +785,42 @@ handle_ping(xmlNode *msg)
26ad17
 }
26ad17
 
26ad17
 /*!
26ad17
+ * \brief Handle a PCMK__CONTROLD_CMD_NODES message
26ad17
+ *
26ad17
+ * \return Next FSA input
26ad17
+ */
26ad17
+static enum crmd_fsa_input
26ad17
+handle_node_list(xmlNode *request)
26ad17
+{
26ad17
+    GHashTableIter iter;
26ad17
+    crm_node_t *node = NULL;
26ad17
+    xmlNode *reply = NULL;
26ad17
+    xmlNode *reply_data = NULL;
26ad17
+
26ad17
+    // Create message data for reply
26ad17
+    reply_data = create_xml_node(NULL, XML_CIB_TAG_NODES);
26ad17
+    g_hash_table_iter_init(&iter, crm_peer_cache);
26ad17
+    while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
26ad17
+        xmlNode *xml = create_xml_node(reply_data, XML_CIB_TAG_NODE);
26ad17
+
26ad17
+        crm_xml_add_ll(xml, XML_ATTR_ID, (long long) node->id); // uint32_t
26ad17
+        crm_xml_add(xml, XML_ATTR_UNAME, node->uname);
26ad17
+        crm_xml_add(xml, XML_NODE_IN_CLUSTER, node->state);
26ad17
+    }
26ad17
+
26ad17
+    // Create and send reply
26ad17
+    reply = create_reply(request, reply_data);
26ad17
+    free_xml(reply_data);
26ad17
+    if (reply) {
26ad17
+        (void) relay_message(reply, TRUE);
26ad17
+        free_xml(reply);
26ad17
+    }
26ad17
+
26ad17
+    // Nothing further to do
26ad17
+    return I_NULL;
26ad17
+}
26ad17
+
26ad17
+/*!
26ad17
  * \brief Handle a CRM_OP_NODE_INFO request
26ad17
  *
26ad17
  * \param[in] msg  Message XML
26ad17
@@ -1080,6 +1117,9 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
26ad17
 
26ad17
         remote_ra_process_maintenance_nodes(xml);
26ad17
 
26ad17
+    } else if (strcmp(op, PCMK__CONTROLD_CMD_NODES) == 0) {
26ad17
+        return handle_node_list(stored_msg);
26ad17
+
26ad17
         /*========== (NOT_DC)-Only Actions ==========*/
26ad17
     } else if (!AM_I_DC) {
26ad17
 
26ad17
diff --git a/include/crm/common/ipc_controld.h b/include/crm/common/ipc_controld.h
26ad17
index 0ebabfc..b817357 100644
26ad17
--- a/include/crm/common/ipc_controld.h
26ad17
+++ b/include/crm/common/ipc_controld.h
26ad17
@@ -22,6 +22,7 @@ extern "C" {
26ad17
  */
26ad17
 
26ad17
 #include <stdbool.h>                    // bool
26ad17
+#include <glib.h>                       // GList
26ad17
 #include <libxml/tree.h>                // xmlNode
26ad17
 #include <crm/common/ipc.h>             // pcmk_ipc_api_t
26ad17
 
26ad17
@@ -32,8 +33,16 @@ enum pcmk_controld_api_reply {
26ad17
     pcmk_controld_reply_info,
26ad17
     pcmk_controld_reply_resource,
26ad17
     pcmk_controld_reply_ping,
26ad17
+    pcmk_controld_reply_nodes,
26ad17
 };
26ad17
 
26ad17
+// Node information passed with pcmk_controld_reply_nodes
26ad17
+typedef struct {
26ad17
+    uint32_t id;
26ad17
+    const char *uname;
26ad17
+    const char *state;
26ad17
+} pcmk_controld_api_node_t;
26ad17
+
26ad17
 /*!
26ad17
  * Controller reply passed to event callback
26ad17
  *
26ad17
@@ -72,6 +81,9 @@ typedef struct {
26ad17
             const char *fsa_state;
26ad17
             const char *result;
26ad17
         } ping;
26ad17
+
26ad17
+        // pcmk_controld_reply_nodes
26ad17
+        GList *nodes; // list of pcmk_controld_api_node_t *
26ad17
     } data;
26ad17
 } pcmk_controld_api_reply_t;
26ad17
 
26ad17
@@ -88,6 +100,7 @@ int pcmk_controld_api_refresh(pcmk_ipc_api_t *api, const char *target_node,
26ad17
                               const char *provider, const char *type,
26ad17
                               bool cib_only);
26ad17
 int pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name);
26ad17
+int pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api);
26ad17
 int pcmk_controld_api_shutdown(pcmk_ipc_api_t *api, const char *node_name);
26ad17
 int pcmk_controld_api_start_election(pcmk_ipc_api_t *api);
26ad17
 unsigned int pcmk_controld_api_replies_expected(pcmk_ipc_api_t *api);
26ad17
diff --git a/include/crm_internal.h b/include/crm_internal.h
26ad17
index fd56fc6..cf8999f 100644
26ad17
--- a/include/crm_internal.h
26ad17
+++ b/include/crm_internal.h
26ad17
@@ -122,6 +122,7 @@ pid_t pcmk_locate_sbd(void);
26ad17
 #define PCMK__ATTRD_CMD_SYNC_RESPONSE   "sync-response"
26ad17
 #define PCMK__ATTRD_CMD_CLEAR_FAILURE   "clear-failure"
26ad17
 
26ad17
+#define PCMK__CONTROLD_CMD_NODES        "list-nodes"
26ad17
 
26ad17
 /*
26ad17
  * Environment variables used by Pacemaker
26ad17
diff --git a/lib/common/ipc_controld.c b/lib/common/ipc_controld.c
26ad17
index 22bb733..a733dd3 100644
26ad17
--- a/lib/common/ipc_controld.c
26ad17
+++ b/lib/common/ipc_controld.c
26ad17
@@ -120,6 +120,28 @@ set_ping_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
26ad17
     data->data.ping.result = crm_element_value(msg_data, XML_PING_ATTR_STATUS);
26ad17
 }
26ad17
 
26ad17
+static void
26ad17
+set_nodes_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
26ad17
+{
26ad17
+    pcmk_controld_api_node_t *node_info;
26ad17
+
26ad17
+    data->reply_type = pcmk_controld_reply_nodes;
26ad17
+    for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE);
26ad17
+         node != NULL; node = crm_next_same_xml(node)) {
26ad17
+
26ad17
+        long long id_ll = 0;
26ad17
+
26ad17
+        node_info = calloc(1, sizeof(pcmk_controld_api_node_t));
26ad17
+        crm_element_value_ll(node, XML_ATTR_ID, &id_ll);
26ad17
+        if (id_ll > 0) {
26ad17
+            node_info->id = id_ll;
26ad17
+        }
26ad17
+        node_info->uname = crm_element_value(node, XML_ATTR_UNAME);
26ad17
+        node_info->state = crm_element_value(node, XML_NODE_IN_CLUSTER);
26ad17
+        data->data.nodes = g_list_prepend(data->data.nodes, node_info);
26ad17
+    }
26ad17
+}
26ad17
+
26ad17
 static bool
26ad17
 reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
26ad17
 {
26ad17
@@ -201,6 +223,9 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
26ad17
         } else if (!strcmp(value, CRM_OP_PING)) {
26ad17
             set_ping_data(&reply_data, msg_data);
26ad17
 
26ad17
+        } else if (!strcmp(value, PCMK__CONTROLD_CMD_NODES)) {
26ad17
+            set_nodes_data(&reply_data, msg_data);
26ad17
+
26ad17
         } else {
26ad17
             crm_debug("Unrecognizable controller message: unknown command '%s'",
26ad17
                       value);
26ad17
@@ -210,6 +235,11 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
26ad17
     }
26ad17
 
26ad17
     pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
26ad17
+
26ad17
+    // Free any reply data that was allocated
26ad17
+    if (safe_str_eq(value, PCMK__CONTROLD_CMD_NODES)) {
26ad17
+        g_list_free_full(reply_data.data.nodes, free);
26ad17
+    }
26ad17
 }
26ad17
 
26ad17
 pcmk__ipc_methods_t *
26ad17
@@ -376,6 +406,29 @@ pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name)
26ad17
 }
26ad17
 
26ad17
 /*!
26ad17
+ * \brief Ask the controller for cluster information
26ad17
+ *
26ad17
+ * \param[in] api          Controller connection
26ad17
+ *
26ad17
+ * \return Standard Pacemaker return code
26ad17
+ * \note Event callback will get a reply of type pcmk_controld_reply_nodes.
26ad17
+ */
26ad17
+int
26ad17
+pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api)
26ad17
+{
26ad17
+    xmlNode *request;
26ad17
+    int rc = EINVAL;
26ad17
+
26ad17
+    request = create_controller_request(api, PCMK__CONTROLD_CMD_NODES, NULL,
26ad17
+                                        NULL);
26ad17
+    if (request != NULL) {
26ad17
+        rc = send_controller_request(api, request, true);
26ad17
+        free_xml(request);
26ad17
+    }
26ad17
+    return rc;
26ad17
+}
26ad17
+
26ad17
+/*!
26ad17
  * \internal
26ad17
  * \brief Ask the controller to shut down
26ad17
  *
26ad17
-- 
26ad17
1.8.3.1
26ad17
26ad17
26ad17
From 74e2d8d18bf534c1ec6f0e0f44a90772d393a553 Mon Sep 17 00:00:00 2001
26ad17
From: Ken Gaillot <kgaillot@redhat.com>
26ad17
Date: Thu, 2 Jul 2020 11:51:56 -0500
26ad17
Subject: [PATCH 2/3] Refactor: functionize numeric comparisons of strings
26ad17
26ad17
This moves the guts of sort_node_uname() from libpe_status to a new function,
26ad17
pcmk_numeric_strcasecmp(), in libcrmcommon, so it can be used with strings and
26ad17
not just pe_node_t objects.
26ad17
---
26ad17
 include/crm/common/util.h |  1 +
26ad17
 lib/common/strings.c      | 65 +++++++++++++++++++++++++++++++++++++++++++++++
26ad17
 lib/pengine/utils.c       | 50 ++----------------------------------
26ad17
 3 files changed, 68 insertions(+), 48 deletions(-)
26ad17
26ad17
diff --git a/include/crm/common/util.h b/include/crm/common/util.h
26ad17
index 67d74d2..bb97b0a 100644
26ad17
--- a/include/crm/common/util.h
26ad17
+++ b/include/crm/common/util.h
26ad17
@@ -59,6 +59,7 @@ gboolean crm_strcase_equal(gconstpointer a, gconstpointer b);
26ad17
 char *crm_strdup_printf(char const *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
26ad17
 int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end);
26ad17
 gboolean pcmk__str_in_list(GList *lst, const gchar *s);
26ad17
+int pcmk_numeric_strcasecmp(const char *s1, const char *s2);
26ad17
 
26ad17
 #  define safe_str_eq(a, b) crm_str_eq(a, b, FALSE)
26ad17
 #  define crm_str_hash g_str_hash_traditional
26ad17
diff --git a/lib/common/strings.c b/lib/common/strings.c
26ad17
index 4562738..bd68ccf 100644
26ad17
--- a/lib/common/strings.c
26ad17
+++ b/lib/common/strings.c
26ad17
@@ -16,6 +16,7 @@
26ad17
 #include <stdio.h>
26ad17
 #include <string.h>
26ad17
 #include <stdlib.h>
26ad17
+#include <ctype.h>
26ad17
 #include <limits.h>
26ad17
 #include <bzlib.h>
26ad17
 #include <sys/types.h>
26ad17
@@ -715,3 +716,67 @@ pcmk__str_none_of(const char *s, ...)
26ad17
 
26ad17
     return g_list_find_custom(lst, s, (GCompareFunc) strcmp) != NULL;
26ad17
 }
26ad17
+
26ad17
+/*
26ad17
+ * \brief Sort strings, with numeric portions sorted numerically
26ad17
+ *
26ad17
+ * Sort two strings case-insensitively like strcasecmp(), but with any numeric
26ad17
+ * portions of the string sorted numerically. This is particularly useful for
26ad17
+ * node names (for example, "node10" will sort higher than "node9" but lower
26ad17
+ * than "remotenode9").
26ad17
+ *
26ad17
+ * \param[in] s1  First string to compare (must not be NULL)
26ad17
+ * \param[in] s2  Second string to compare (must not be NULL)
26ad17
+ *
26ad17
+ * \retval -1 \p s1 comes before \p s2
26ad17
+ * \retval  0 \p s1 and \p s2 are equal
26ad17
+ * \retval  1 \p s1 comes after \p s2
26ad17
+ */
26ad17
+int
26ad17
+pcmk_numeric_strcasecmp(const char *s1, const char *s2)
26ad17
+{
26ad17
+    while (*s1 && *s2) {
26ad17
+        if (isdigit(*s1) && isdigit(*s2)) {
26ad17
+            // If node names contain a number, sort numerically
26ad17
+
26ad17
+            char *end1 = NULL;
26ad17
+            char *end2 = NULL;
26ad17
+            long num1 = strtol(s1, &end1, 10);
26ad17
+            long num2 = strtol(s2, &end2, 10);
26ad17
+
26ad17
+            // allow ordering e.g. 007 > 7
26ad17
+            size_t len1 = end1 - s1;
26ad17
+            size_t len2 = end2 - s2;
26ad17
+
26ad17
+            if (num1 < num2) {
26ad17
+                return -1;
26ad17
+            } else if (num1 > num2) {
26ad17
+                return 1;
26ad17
+            } else if (len1 < len2) {
26ad17
+                return -1;
26ad17
+            } else if (len1 > len2) {
26ad17
+                return 1;
26ad17
+            }
26ad17
+            s1 = end1;
26ad17
+            s2 = end2;
26ad17
+        } else {
26ad17
+            // Compare non-digits case-insensitively
26ad17
+            int lower1 = tolower(*s1);
26ad17
+            int lower2 = tolower(*s2);
26ad17
+
26ad17
+            if (lower1 < lower2) {
26ad17
+                return -1;
26ad17
+            } else if (lower1 > lower2) {
26ad17
+                return 1;
26ad17
+            }
26ad17
+            ++s1;
26ad17
+            ++s2;
26ad17
+        }
26ad17
+    }
26ad17
+    if (!*s1 && *s2) {
26ad17
+        return -1;
26ad17
+    } else if (*s1 && !*s2) {
26ad17
+        return 1;
26ad17
+    }
26ad17
+    return 0;
26ad17
+}
26ad17
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
26ad17
index ce3c260..584def4 100644
26ad17
--- a/lib/pengine/utils.c
26ad17
+++ b/lib/pengine/utils.c
26ad17
@@ -13,7 +13,6 @@
26ad17
 #include <crm/common/xml.h>
26ad17
 #include <crm/common/util.h>
26ad17
 
26ad17
-#include <ctype.h>
26ad17
 #include <glib.h>
26ad17
 #include <stdbool.h>
26ad17
 
26ad17
@@ -214,53 +213,8 @@ pe__node_list2table(GList *list)
26ad17
 gint
26ad17
 sort_node_uname(gconstpointer a, gconstpointer b)
26ad17
 {
26ad17
-    const char *name_a = ((const pe_node_t *) a)->details->uname;
26ad17
-    const char *name_b = ((const pe_node_t *) b)->details->uname;
26ad17
-
26ad17
-    while (*name_a && *name_b) {
26ad17
-        if (isdigit(*name_a) && isdigit(*name_b)) {
26ad17
-            // If node names contain a number, sort numerically
26ad17
-
26ad17
-            char *end_a = NULL;
26ad17
-            char *end_b = NULL;
26ad17
-            long num_a = strtol(name_a, &end_a, 10);
26ad17
-            long num_b = strtol(name_b, &end_b, 10);
26ad17
-
26ad17
-            // allow ordering e.g. 007 > 7
26ad17
-            size_t len_a = end_a - name_a;
26ad17
-            size_t len_b = end_b - name_b;
26ad17
-
26ad17
-            if (num_a < num_b) {
26ad17
-                return -1;
26ad17
-            } else if (num_a > num_b) {
26ad17
-                return 1;
26ad17
-            } else if (len_a < len_b) {
26ad17
-                return -1;
26ad17
-            } else if (len_a > len_b) {
26ad17
-                return 1;
26ad17
-            }
26ad17
-            name_a = end_a;
26ad17
-            name_b = end_b;
26ad17
-        } else {
26ad17
-            // Compare non-digits case-insensitively
26ad17
-            int lower_a = tolower(*name_a);
26ad17
-            int lower_b = tolower(*name_b);
26ad17
-
26ad17
-            if (lower_a < lower_b) {
26ad17
-                return -1;
26ad17
-            } else if (lower_a > lower_b) {
26ad17
-                return 1;
26ad17
-            }
26ad17
-            ++name_a;
26ad17
-            ++name_b;
26ad17
-        }
26ad17
-    }
26ad17
-    if (!*name_a && *name_b) {
26ad17
-        return -1;
26ad17
-    } else if (*name_a && !*name_b) {
26ad17
-        return 1;
26ad17
-    }
26ad17
-    return 0;
26ad17
+    return pcmk_numeric_strcasecmp(((const pe_node_t *) a)->details->uname,
26ad17
+                                   ((const pe_node_t *) b)->details->uname);
26ad17
 }
26ad17
 
26ad17
 /*!
26ad17
-- 
26ad17
1.8.3.1
26ad17
26ad17
26ad17
From 8461509158e06365122dc741c527c83c94e966ce Mon Sep 17 00:00:00 2001
26ad17
From: Ken Gaillot <kgaillot@redhat.com>
26ad17
Date: Fri, 24 Apr 2020 19:35:19 -0500
26ad17
Subject: [PATCH 3/3] Fix: tools: crm_node -l and -p now work from Pacemaker
26ad17
 Remote nodes
26ad17
26ad17
crm_node now asks the controller for the cluster node list, instead of
26ad17
pacemakerd. This allows it to work from Pacemaker Remote nodes, since
26ad17
controller IPC is proxied but pacemakerd IPC is not.
26ad17
---
26ad17
 tools/crm_node.c | 176 +++++++++++++++++++++----------------------------------
26ad17
 1 file changed, 67 insertions(+), 109 deletions(-)
26ad17
26ad17
diff --git a/tools/crm_node.c b/tools/crm_node.c
26ad17
index 57c2ee5..146454d 100644
26ad17
--- a/tools/crm_node.c
26ad17
+++ b/tools/crm_node.c
26ad17
@@ -130,6 +130,16 @@ remove_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError *
26ad17
     return TRUE;
26ad17
 }
26ad17
 
26ad17
+static gint
26ad17
+sort_node(gconstpointer a, gconstpointer b)
26ad17
+{
26ad17
+    const pcmk_controld_api_node_t *node_a = a;
26ad17
+    const pcmk_controld_api_node_t *node_b = b;
26ad17
+
26ad17
+    return pcmk_numeric_strcasecmp((node_a->uname? node_a->uname : ""),
26ad17
+                                   (node_b->uname? node_b->uname : ""));
26ad17
+}
26ad17
+
26ad17
 static void
26ad17
 controller_event_cb(pcmk_ipc_api_t *controld_api,
26ad17
                     enum pcmk_ipc_event event_type, crm_exit_t status,
26ad17
@@ -157,15 +167,16 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
26ad17
                 crm_exit_str(status));
26ad17
         goto done;
26ad17
     }
26ad17
-    if (reply->reply_type != pcmk_controld_reply_info) {
26ad17
-        fprintf(stderr, "error: Unknown reply type %d from controller\n",
26ad17
-                reply->reply_type);
26ad17
-        goto done;
26ad17
-    }
26ad17
 
26ad17
     // Parse desired info from reply and display to user
26ad17
     switch (options.command) {
26ad17
         case 'i':
26ad17
+            if (reply->reply_type != pcmk_controld_reply_info) {
26ad17
+                fprintf(stderr,
26ad17
+                        "error: Unknown reply type %d from controller\n",
26ad17
+                        reply->reply_type);
26ad17
+                goto done;
26ad17
+            }
26ad17
             if (reply->data.node_info.id == 0) {
26ad17
                 fprintf(stderr,
26ad17
                         "error: Controller reply did not contain node ID\n");
26ad17
@@ -177,6 +188,12 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
26ad17
 
26ad17
         case 'n':
26ad17
         case 'N':
26ad17
+            if (reply->reply_type != pcmk_controld_reply_info) {
26ad17
+                fprintf(stderr,
26ad17
+                        "error: Unknown reply type %d from controller\n",
26ad17
+                        reply->reply_type);
26ad17
+                goto done;
26ad17
+            }
26ad17
             if (reply->data.node_info.uname == NULL) {
26ad17
                 fprintf(stderr, "Node is not known to cluster\n");
26ad17
                 exit_code = CRM_EX_NOHOST;
26ad17
@@ -186,6 +203,12 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
26ad17
             break;
26ad17
 
26ad17
         case 'q':
26ad17
+            if (reply->reply_type != pcmk_controld_reply_info) {
26ad17
+                fprintf(stderr,
26ad17
+                        "error: Unknown reply type %d from controller\n",
26ad17
+                        reply->reply_type);
26ad17
+                goto done;
26ad17
+            }
26ad17
             printf("%d\n", reply->data.node_info.have_quorum);
26ad17
             if (!(reply->data.node_info.have_quorum)) {
26ad17
                 exit_code = CRM_EX_QUORUM;
26ad17
@@ -193,6 +216,36 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
26ad17
             }
26ad17
             break;
26ad17
 
26ad17
+        case 'l':
26ad17
+        case 'p':
26ad17
+            if (reply->reply_type != pcmk_controld_reply_nodes) {
26ad17
+                fprintf(stderr,
26ad17
+                        "error: Unknown reply type %d from controller\n",
26ad17
+                        reply->reply_type);
26ad17
+                goto done;
26ad17
+            }
26ad17
+            reply->data.nodes = g_list_sort(reply->data.nodes, sort_node);
26ad17
+            for (GList *node_iter = reply->data.nodes;
26ad17
+                 node_iter != NULL; node_iter = node_iter->next) {
26ad17
+
26ad17
+                pcmk_controld_api_node_t *node = node_iter->data;
26ad17
+                const char *uname = (node->uname? node->uname : "");
26ad17
+                const char *state = (node->state? node->state : "");
26ad17
+
26ad17
+                if (options.command == 'l') {
26ad17
+                    printf("%lu %s %s\n",
26ad17
+                           (unsigned long) node->id, uname, state);
26ad17
+
26ad17
+                // i.e. CRM_NODE_MEMBER, but we don't want to include cluster.h
26ad17
+                } else if (!strcmp(state, "member")) {
26ad17
+                    printf("%s ", uname);
26ad17
+                }
26ad17
+            }
26ad17
+            if (options.command == 'p') {
26ad17
+                printf("\n");
26ad17
+            }
26ad17
+            break;
26ad17
+
26ad17
         default:
26ad17
             fprintf(stderr, "internal error: Controller reply not expected\n");
26ad17
             exit_code = CRM_EX_SOFTWARE;
26ad17
@@ -207,7 +260,7 @@ done:
26ad17
 }
26ad17
 
26ad17
 static void
26ad17
-run_controller_mainloop(uint32_t nodeid)
26ad17
+run_controller_mainloop(uint32_t nodeid, bool list_nodes)
26ad17
 {
26ad17
     pcmk_ipc_api_t *controld_api = NULL;
26ad17
     int rc;
26ad17
@@ -233,7 +286,11 @@ run_controller_mainloop(uint32_t nodeid)
26ad17
         return;
26ad17
     }
26ad17
 
26ad17
-    rc = pcmk_controld_api_node_info(controld_api, nodeid);
26ad17
+    if (list_nodes) {
26ad17
+        rc = pcmk_controld_api_list_nodes(controld_api);
26ad17
+    } else {
26ad17
+        rc = pcmk_controld_api_node_info(controld_api, nodeid);
26ad17
+    }
26ad17
     if (rc != pcmk_rc_ok) {
26ad17
         fprintf(stderr, "error: Could not ping controller: %s\n",
26ad17
                 pcmk_rc_str(rc));
26ad17
@@ -263,7 +320,7 @@ print_node_name(void)
26ad17
 
26ad17
     } else {
26ad17
         // Otherwise ask the controller
26ad17
-        run_controller_mainloop(0);
26ad17
+        run_controller_mainloop(0, false);
26ad17
     }
26ad17
 }
26ad17
 
26ad17
@@ -444,105 +501,6 @@ remove_node(const char *target_uname)
26ad17
     exit_code = CRM_EX_OK;
26ad17
 }
26ad17
 
26ad17
-static gint
26ad17
-compare_node_xml(gconstpointer a, gconstpointer b)
26ad17
-{
26ad17
-    const char *a_name = crm_element_value((xmlNode*) a, "uname");
26ad17
-    const char *b_name = crm_element_value((xmlNode*) b, "uname");
26ad17
-
26ad17
-    return strcmp((a_name? a_name : ""), (b_name? b_name : ""));
26ad17
-}
26ad17
-
26ad17
-static int
26ad17
-node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata)
26ad17
-{
26ad17
-    GList *nodes = NULL;
26ad17
-    xmlNode *node = NULL;
26ad17
-    xmlNode *msg = string2xml(buffer);
26ad17
-    const char *uname;
26ad17
-    const char *state;
26ad17
-
26ad17
-    if (msg == NULL) {
26ad17
-        fprintf(stderr, "error: Could not understand pacemakerd response\n");
26ad17
-        exit_code = CRM_EX_PROTOCOL;
26ad17
-        g_main_loop_quit(mainloop);
26ad17
-        return 0;
26ad17
-    }
26ad17
-
26ad17
-    crm_log_xml_trace(msg, "message");
26ad17
-
26ad17
-    for (node = __xml_first_child(msg); node != NULL; node = __xml_next(node)) {
26ad17
-        nodes = g_list_insert_sorted(nodes, node, compare_node_xml);
26ad17
-    }
26ad17
-
26ad17
-    for (GList *iter = nodes; iter; iter = iter->next) {
26ad17
-        node = (xmlNode*) iter->data;
26ad17
-        uname = crm_element_value(node, "uname");
26ad17
-        state = crm_element_value(node, "state");
26ad17
-
26ad17
-        if (options.command == 'l') {
26ad17
-            int id = 0;
26ad17
-
26ad17
-            crm_element_value_int(node, "id", &id;;
26ad17
-            printf("%d %s %s\n", id, (uname? uname : ""), (state? state : ""));
26ad17
-
26ad17
-        // This is CRM_NODE_MEMBER but we don't want to include cluster header
26ad17
-        } else if ((options.command == 'p') && safe_str_eq(state, "member")) {
26ad17
-            printf("%s ", (uname? uname : ""));
26ad17
-        }
26ad17
-    }
26ad17
-    if (options.command == 'p') {
26ad17
-        fprintf(stdout, "\n");
26ad17
-    }
26ad17
-
26ad17
-    free_xml(msg);
26ad17
-    exit_code = CRM_EX_OK;
26ad17
-    g_main_loop_quit(mainloop);
26ad17
-    return 0;
26ad17
-}
26ad17
-
26ad17
-static void
26ad17
-lost_pacemakerd(gpointer user_data)
26ad17
-{
26ad17
-    fprintf(stderr, "error: Lost connection to cluster\n");
26ad17
-    exit_code = CRM_EX_DISCONNECT;
26ad17
-    g_main_loop_quit(mainloop);
26ad17
-}
26ad17
-
26ad17
-static void
26ad17
-run_pacemakerd_mainloop(void)
26ad17
-{
26ad17
-    crm_ipc_t *ipc = NULL;
26ad17
-    xmlNode *poke = NULL;
26ad17
-    mainloop_io_t *source = NULL;
26ad17
-
26ad17
-    struct ipc_client_callbacks ipc_callbacks = {
26ad17
-        .dispatch = node_mcp_dispatch,
26ad17
-        .destroy = lost_pacemakerd
26ad17
-    };
26ad17
-
26ad17
-    source = mainloop_add_ipc_client(CRM_SYSTEM_MCP, G_PRIORITY_DEFAULT, 0,
26ad17
-                                     NULL, &ipc_callbacks);
26ad17
-    ipc = mainloop_get_ipc_client(source);
26ad17
-    if (ipc == NULL) {
26ad17
-        fprintf(stderr,
26ad17
-                "error: Could not connect to cluster (is it running?)\n");
26ad17
-        exit_code = CRM_EX_DISCONNECT;
26ad17
-        return;
26ad17
-    }
26ad17
-
26ad17
-    // Sending anything will get us a list of nodes
26ad17
-    poke = create_xml_node(NULL, "poke");
26ad17
-    crm_ipc_send(ipc, poke, 0, 0, NULL);
26ad17
-    free_xml(poke);
26ad17
-
26ad17
-    // Handle reply via node_mcp_dispatch()
26ad17
-    mainloop = g_main_loop_new(NULL, FALSE);
26ad17
-    g_main_loop_run(mainloop);
26ad17
-    g_main_loop_unref(mainloop);
26ad17
-    mainloop = NULL;
26ad17
-}
26ad17
-
26ad17
 static GOptionContext *
26ad17
 build_arg_context(pcmk__common_args_t *args, GOptionGroup *group) {
26ad17
     GOptionContext *context = NULL;
26ad17
@@ -627,11 +585,11 @@ main(int argc, char **argv)
26ad17
         case 'i':
26ad17
         case 'q':
26ad17
         case 'N':
26ad17
-            run_controller_mainloop(options.nodeid);
26ad17
+            run_controller_mainloop(options.nodeid, false);
26ad17
             break;
26ad17
         case 'l':
26ad17
         case 'p':
26ad17
-            run_pacemakerd_mainloop();
26ad17
+            run_controller_mainloop(0, true);
26ad17
             break;
26ad17
         default:
26ad17
             break;
26ad17
-- 
26ad17
1.8.3.1
26ad17