diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c
index c705b7ff98..1d1e775392 100644
--- a/lib/pacemaker/pcmk_cluster_queries.c
+++ b/lib/pacemaker/pcmk_cluster_queries.c
@@ -1,483 +1,507 @@
 #include <glib.h>               // gboolean, GMainLoop, etc.
 #include <libxml/tree.h>        // xmlNode
 
 #include <pacemaker.h>
 #include <pacemaker-internal.h>
 
 #include <crm/crm.h>
 #include <crm/cib.h>
 #include <crm/msg_xml.h>
 #include <crm/common/output_internal.h>
 #include <crm/common/xml.h>
 #include <crm/common/iso8601.h>
 #include <crm/common/ipc_controld.h>
 #include <crm/common/ipc_pacemakerd.h>
 #include <crm/common/mainloop.h>
 
 #define DEFAULT_MESSAGE_TIMEOUT_MS 30000
 
 
 typedef struct {
     pcmk__output_t *out;
     GMainLoop *mainloop;
     int rc;
     guint message_timer_id;
     guint message_timeout_ms;
 } data_t;
 
 static void
 quit_main_loop(data_t *data)
 {
     if (data->mainloop != NULL) {
         GMainLoop *mloop = data->mainloop;
 
         data->mainloop = NULL; // Don't re-enter this block
         pcmk_quit_main_loop(mloop, 10);
         g_main_loop_unref(mloop);
     }
 }
 
 static gboolean
 admin_message_timeout(gpointer user_data)
 {
     data_t *data = user_data;
     pcmk__output_t *out = data->out;
 
     out->err(out, "error: No reply received from controller before timeout (%dms)",
             data->message_timeout_ms);
     data->message_timer_id = 0;
     data->rc = ETIMEDOUT;
     quit_main_loop(data);
     return FALSE; // Tells glib to remove source
 }
 
 static void
 start_main_loop(data_t *data)
 {
     if (data->message_timeout_ms < 1) {
         data->message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
     }
 
     data->rc = ECONNRESET; // For unexpected disconnects
     data->mainloop = g_main_loop_new(NULL, FALSE);
     data->message_timer_id = g_timeout_add(data->message_timeout_ms,
                                      admin_message_timeout,
                                      data);
     g_main_loop_run(data->mainloop);
 }
 
 static void
 event_done(data_t *data, pcmk_ipc_api_t *api)
 {
     pcmk_disconnect_ipc(api);
     quit_main_loop(data);
 }
 
 static pcmk_controld_api_reply_t *
 controld_event_reply(data_t *data, pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
 {
     pcmk__output_t *out = data->out;
     pcmk_controld_api_reply_t *reply = event_data;
 
     switch (event_type) {
         case pcmk_ipc_event_disconnect:
             if (data->rc == ECONNRESET) { // Unexpected
                 out->err(out, "error: Lost connection to controller");
             }
             event_done(data, controld_api);
             return NULL;
 
         case pcmk_ipc_event_reply:
             break;
 
         default:
             return NULL;
     }
 
     if (data->message_timer_id != 0) {
         g_source_remove(data->message_timer_id);
         data->message_timer_id = 0;
     }
 
     if (status != CRM_EX_OK) {
         out->err(out, "error: Bad reply from controller: %s",
                 crm_exit_str(status));
         data->rc = EBADMSG;
         event_done(data, controld_api);
         return NULL;
     }
 
     if (reply->reply_type != pcmk_controld_reply_ping) {
         out->err(out, "error: Unknown reply type %d from controller",
                 reply->reply_type);
         data->rc = EBADMSG;
         event_done(data, controld_api);
         return NULL;
     }
 
     return reply;
 }
 
 static void
 controller_status_event_cb(pcmk_ipc_api_t *controld_api,
                     enum pcmk_ipc_event event_type, crm_exit_t status,
                     void *event_data, void *user_data)
 {
     data_t *data = user_data;
     pcmk__output_t *out = data->out;
     pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
         event_type, status, event_data);
 
     if (reply != NULL) {
         out->message(out, "health",
                reply->data.ping.sys_from,
                reply->host_from,
                reply->data.ping.fsa_state,
                reply->data.ping.result);
         data->rc = pcmk_rc_ok;
     }
 
     event_done(data, controld_api);
 }
 
 static void
 designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
                     enum pcmk_ipc_event event_type, crm_exit_t status,
                     void *event_data, void *user_data)
 {
     data_t *data = user_data;
     pcmk__output_t *out = data->out;
     pcmk_controld_api_reply_t *reply = controld_event_reply(data, controld_api,
         event_type, status, event_data);
 
     if (reply != NULL) {
         out->message(out, "dc", reply->host_from);
         data->rc = pcmk_rc_ok;
     }
 
     event_done(data, controld_api);
 }
 
 static void
 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
                     enum pcmk_ipc_event event_type, crm_exit_t status,
                     void *event_data, void *user_data)
 {
     data_t *data = user_data;
     pcmk__output_t *out = data->out;
     pcmk_pacemakerd_api_reply_t *reply = event_data;
 
     crm_time_t *crm_when;
     char *pinged_buf = NULL;
 
     switch (event_type) {
         case pcmk_ipc_event_disconnect:
             if (data->rc == ECONNRESET) { // Unexpected
                 out->err(out, "error: Lost connection to pacemakerd");
             }
             event_done(data, pacemakerd_api);
             return;
 
         case pcmk_ipc_event_reply:
             break;
 
         default:
             return;
     }
 
     if (data->message_timer_id != 0) {
         g_source_remove(data->message_timer_id);
         data->message_timer_id = 0;
     }
 
     if (status != CRM_EX_OK) {
         out->err(out, "error: Bad reply from pacemakerd: %s",
                 crm_exit_str(status));
         event_done(data, pacemakerd_api);
         return;
     }
 
     if (reply->reply_type != pcmk_pacemakerd_reply_ping) {
         out->err(out, "error: Unknown reply type %d from pacemakerd",
                 reply->reply_type);
         event_done(data, pacemakerd_api);
         return;
     }
 
     // Parse desired information from reply
     crm_when = crm_time_new(NULL);
     crm_time_set_timet(crm_when, &reply->data.ping.last_good);
     pinged_buf = crm_time_as_string(crm_when,
         crm_time_log_date | crm_time_log_timeofday |
             crm_time_log_with_timezone);
 
     out->message(out, "pacemakerd-health",
         reply->data.ping.sys_from,
         (reply->data.ping.status == pcmk_rc_ok)?
             pcmk_pacemakerd_api_daemon_state_enum2text(
                 reply->data.ping.state):"query failed",
         (reply->data.ping.status == pcmk_rc_ok)?pinged_buf:"");
     data->rc = pcmk_rc_ok;
     crm_time_free(crm_when);
     free(pinged_buf);
 
     event_done(data, pacemakerd_api);
 }
 
 static pcmk_ipc_api_t *
 ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb)
 {
     int rc;
     pcmk__output_t *out = data->out;
     pcmk_ipc_api_t *api = NULL;
 
 
     rc = pcmk_new_ipc_api(&api, server);
     if (api == NULL) {
         out->err(out, "error: Could not connect to %s: %s",
                 pcmk_ipc_name(api, true),
                 pcmk_rc_str(rc));
         data->rc = rc;
         return NULL;
     }
     if (cb != NULL) {
         pcmk_register_ipc_callback(api, cb, data);
     }
     rc = pcmk_connect_ipc(api, pcmk_ipc_dispatch_main);
     if (rc != pcmk_rc_ok) {
         out->err(out, "error: Could not connect to %s: %s",
                 pcmk_ipc_name(api, true),
                 pcmk_rc_str(rc));
         data->rc = rc;
         return NULL;
     }
 
     return api;
 }
 
 int
 pcmk__controller_status(pcmk__output_t *out, char *dest_node, guint message_timeout_ms)
 {
     data_t data = {
         .out = out,
         .mainloop = NULL,
         .rc = pcmk_rc_ok,
         .message_timer_id = 0,
         .message_timeout_ms = message_timeout_ms
     };
     pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, controller_status_event_cb);
 
     if (controld_api != NULL) {
         int rc = pcmk_controld_api_ping(controld_api, dest_node);
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
             data.rc = rc;
         }
 
         start_main_loop(&data);
 
         pcmk_free_ipc_api(controld_api);
     }
 
     return data.rc;
 }
 
 int
 pcmk_controller_status(xmlNodePtr *xml, char *dest_node, unsigned int message_timeout_ms)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__out_prologue(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__controller_status(out, dest_node, (guint) message_timeout_ms);
     pcmk__out_epilogue(out, xml, rc);
     return rc;
 }
 
 int
 pcmk__designated_controller(pcmk__output_t *out, guint message_timeout_ms)
 {
     data_t data = {
         .out = out,
         .mainloop = NULL,
         .rc = pcmk_rc_ok,
         .message_timer_id = 0,
         .message_timeout_ms = message_timeout_ms
     };
     pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, designated_controller_event_cb);
 
     if (controld_api != NULL) {
         int rc = pcmk_controld_api_ping(controld_api, NULL);
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
             data.rc = rc;
         }
 
         start_main_loop(&data);
 
         pcmk_free_ipc_api(controld_api);
     }
 
     return data.rc;
 }
 
 int
 pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__out_prologue(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__designated_controller(out, (guint) message_timeout_ms);
     pcmk__out_epilogue(out, xml, rc);
     return rc;
 }
 
 int
 pcmk__pacemakerd_status(pcmk__output_t *out, char *ipc_name, guint message_timeout_ms)
 {
     data_t data = {
         .out = out,
         .mainloop = NULL,
         .rc = pcmk_rc_ok,
         .message_timer_id = 0,
         .message_timeout_ms = message_timeout_ms
     };
     pcmk_ipc_api_t *pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd, pacemakerd_event_cb);
 
     if (pacemakerd_api != NULL) {
         int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
             data.rc = rc;
         }
 
         start_main_loop(&data);
 
         pcmk_free_ipc_api(pacemakerd_api);
     }
 
     return data.rc;
 }
 
 int
 pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__out_prologue(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__pacemakerd_status(out, ipc_name, (guint) message_timeout_ms);
     pcmk__out_epilogue(out, xml, rc);
     return rc;
 }
 
 // \return Standard Pacemaker return code
 int
 pcmk__list_nodes(pcmk__output_t *out, gboolean BASH_EXPORT)
 {
     cib_t *the_cib = cib_new();
-    xmlNode *output = NULL;
+    xmlNode *xml_node = NULL;
     int rc;
 
     if (the_cib == NULL) {
         return ENOMEM;
     }
     rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
     if (rc != pcmk_ok) {
         return pcmk_legacy2rc(rc);
     }
 
-    rc = the_cib->cmds->query(the_cib, NULL, &output,
+    rc = the_cib->cmds->query(the_cib, NULL, &xml_node,
                               cib_scope_local | cib_sync_call);
     if (rc == pcmk_ok) {
-        out->message(out, "crmadmin-node-list", output, BASH_EXPORT);
-        free_xml(output);
+        int found = 0;
+        xmlNode *node = NULL;
+        xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node);
+
+        out->begin_list(out, NULL, NULL, "nodes");
+
+        for (node = first_named_child(nodes, XML_CIB_TAG_NODE); node != NULL;
+             node = crm_next_same_xml(node)) {
+            const char *node_type = BASH_EXPORT ? NULL :
+                         crm_element_value(node, XML_ATTR_TYPE);
+            out->message(out, "crmadmin-node", node_type,
+                         crm_str(crm_element_value(node, XML_ATTR_UNAME)),
+                         crm_str(crm_element_value(node, XML_ATTR_ID)),
+                         BASH_EXPORT);
+
+            found++;
+        }
+        // @TODO List Pacemaker Remote nodes that don't have a <node> entry
+
+        out->end_list(out);
+
+        if (found == 0) {
+            out->info(out, "No nodes configured");
+        }
+
+        free_xml(xml_node);
     }
     the_cib->cmds->signoff(the_cib);
     return pcmk_legacy2rc(rc);
 }
 
 #ifdef BUILD_PUBLIC_LIBPACEMAKER
 int
 pcmk_list_nodes(xmlNodePtr *xml)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__out_prologue(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pcmk__register_lib_messages(out);
 
     rc = pcmk__list_nodes(out, FALSE);
     pcmk__out_epilogue(out, xml, rc);
     return rc;
 }
 #endif
 
 // remove when parameters removed from tools/crmadmin.c
 int
 pcmk__shutdown_controller(pcmk__output_t *out, char *dest_node)
 {
     data_t data = {
         .out = out,
         .mainloop = NULL,
         .rc = pcmk_rc_ok,
     };
     pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, NULL);
 
     if (controld_api != NULL) {
         int rc = pcmk_controld_api_shutdown(controld_api, dest_node);
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
             data.rc = rc;
         }
         pcmk_free_ipc_api(controld_api);
     }
 
     return data.rc;
 }
 
 int
 pcmk__start_election(pcmk__output_t *out)
 {
     data_t data = {
         .out = out,
         .mainloop = NULL,
         .rc = pcmk_rc_ok,
     };
     pcmk_ipc_api_t *controld_api = ipc_connect(&data, pcmk_ipc_controld, NULL);
 
     if (controld_api != NULL) {
         int rc = pcmk_controld_api_start_election(controld_api);
         if (rc != pcmk_rc_ok) {
             out->err(out, "error: Command failed: %s", pcmk_rc_str(rc));
             data.rc = rc;
         }
 
         pcmk_free_ipc_api(controld_api);
     }
 
     return data.rc;
 }
diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c
index bc4b91a28a..8f5e3013e7 100644
--- a/lib/pacemaker/pcmk_output.c
+++ b/lib/pacemaker/pcmk_output.c
@@ -1,672 +1,637 @@
 /*
  * Copyright 2019-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.
  */
 
 #include <crm_internal.h>
 #include <crm/common/results.h>
 #include <crm/common/output_internal.h>
 #include <crm/stonith-ng.h>
 #include <crm/fencing/internal.h>
 #include <crm/pengine/internal.h>
 #include <libxml/tree.h>
 #include <pacemaker-internal.h>
 
 pcmk__supported_format_t pcmk__out_formats[] = {
     PCMK__SUPPORTED_FORMAT_XML,
     { NULL, NULL, NULL }
 };
 
 int
 pcmk__out_prologue(pcmk__output_t **out, xmlNodePtr *xml) {
     int rc = pcmk_rc_ok;
 
     if (*xml != NULL) {
         xmlFreeNode(*xml);
     }
 
     pcmk__register_formats(NULL, pcmk__out_formats);
     rc = pcmk__output_new(out, "xml", NULL, NULL);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     return rc;
 }
 
 void
 pcmk__out_epilogue(pcmk__output_t *out, xmlNodePtr *xml, int retval) {
     if (retval == pcmk_rc_ok) {
         out->finish(out, 0, FALSE, (void **) xml);
     }
 
     pcmk__output_free(out);
 }
 
 static char *
 colocations_header(pe_resource_t *rsc, pcmk__colocation_t *cons,
                    gboolean dependents) {
     char *score = NULL;
     char *retval = NULL;
 
     score = score2char(cons->score);
     if (cons->role_rh > RSC_ROLE_STARTED) {
             retval = crm_strdup_printf("%s (score=%s, %s role=%s, id=%s)",
                                        rsc->id, score, dependents ? "needs" : "with",
                                        role2text(cons->role_rh), cons->id);
     } else {
         retval = crm_strdup_printf("%s (score=%s, id=%s)",
                                    rsc->id, score, cons->id);
     }
 
     free(score);
     return retval;
 }
 
 static void
 colocations_xml_node(pcmk__output_t *out, pe_resource_t *rsc,
                      pcmk__colocation_t *cons) {
     char *score = NULL;
     xmlNodePtr node = NULL;
 
     score = score2char(cons->score);
     node = pcmk__output_create_xml_node(out, XML_CONS_TAG_RSC_DEPEND,
                                         "id", cons->id,
                                         "rsc", cons->rsc_lh->id,
                                         "with-rsc", cons->rsc_rh->id,
                                         "score", score,
                                         NULL);
 
     if (cons->node_attribute) {
         xmlSetProp(node, (pcmkXmlStr) "node-attribute", (pcmkXmlStr) cons->node_attribute);
     }
 
     if (cons->role_lh != RSC_ROLE_UNKNOWN) {
         xmlSetProp(node, (pcmkXmlStr) "rsc-role", (pcmkXmlStr) role2text(cons->role_lh));
     }
 
     if (cons->role_rh != RSC_ROLE_UNKNOWN) {
         xmlSetProp(node, (pcmkXmlStr) "with-rsc-role", (pcmkXmlStr) role2text(cons->role_rh));
     }
 
     free(score);
 }
 
 static int
 do_locations_list_xml(pcmk__output_t *out, pe_resource_t *rsc, bool add_header)
 {
     GList *lpc = NULL;
     GList *list = rsc->rsc_location;
     int rc = pcmk_rc_no_output;
 
     for (lpc = list; lpc != NULL; lpc = lpc->next) {
         pe__location_t *cons = lpc->data;
 
         GList *lpc2 = NULL;
 
         for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) {
             pe_node_t *node = (pe_node_t *) lpc2->data;
             char *score = score2char(node->weight);
 
             if (add_header) {
                 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "locations");
             }
 
             pcmk__output_create_xml_node(out, XML_CONS_TAG_RSC_LOCATION,
                                          "node", node->details->uname,
                                          "rsc", rsc->id,
                                          "id", cons->id,
                                          "score", score,
                                          NULL);
             free(score);
         }
     }
 
     if (add_header) {
         PCMK__OUTPUT_LIST_FOOTER(out, rc);
     }
 
     return rc;
 }
 
 PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pe_resource_t *", "gboolean")
 static int
 rsc_is_colocated_with_list(pcmk__output_t *out, va_list args) {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     gboolean recursive = va_arg(args, gboolean);
 
     int rc = pcmk_rc_no_output;
 
     if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
         return rc;
     }
 
     pe__set_resource_flags(rsc, pe_rsc_allocating);
     for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
         pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
         char *hdr = NULL;
 
         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Resources %s is colocated with", rsc->id);
 
         if (pcmk_is_set(cons->rsc_rh->flags, pe_rsc_allocating)) {
             out->list_item(out, NULL, "%s (id=%s - loop)", cons->rsc_rh->id, cons->id);
             continue;
         }
 
         hdr = colocations_header(cons->rsc_rh, cons, FALSE);
         out->list_item(out, NULL, "%s", hdr);
         free(hdr);
 
         /* Empty list header just for indentation of information about this resource. */
         out->begin_list(out, NULL, NULL, NULL);
 
         out->message(out, "locations-list", cons->rsc_rh);
         if (recursive) {
             out->message(out, "rsc-is-colocated-with-list", cons->rsc_rh, recursive);
         }
 
         out->end_list(out);
     }
 
     PCMK__OUTPUT_LIST_FOOTER(out, rc);
     return rc;
 }
 
 PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pe_resource_t *", "gboolean")
 static int
 rsc_is_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     gboolean recursive = va_arg(args, gboolean);
 
     int rc = pcmk_rc_no_output;
 
     if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
         return rc;
     }
 
     pe__set_resource_flags(rsc, pe_rsc_allocating);
     for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
         pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
 
         if (pcmk_is_set(cons->rsc_rh->flags, pe_rsc_allocating)) {
             colocations_xml_node(out, cons->rsc_rh, cons);
             continue;
         }
 
         colocations_xml_node(out, cons->rsc_rh, cons);
         do_locations_list_xml(out, cons->rsc_rh, false);
 
         if (recursive) {
             out->message(out, "rsc-is-colocated-with-list", cons->rsc_rh, recursive);
         }
     }
 
     return rc;
 }
 
 PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pe_resource_t *", "gboolean")
 static int
 rscs_colocated_with_list(pcmk__output_t *out, va_list args) {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     gboolean recursive = va_arg(args, gboolean);
 
     int rc = pcmk_rc_no_output;
 
     if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
         return rc;
     }
 
     pe__set_resource_flags(rsc, pe_rsc_allocating);
     for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
         pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
         char *hdr = NULL;
 
         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Resources colocated with %s", rsc->id);
 
         if (pcmk_is_set(cons->rsc_lh->flags, pe_rsc_allocating)) {
             out->list_item(out, NULL, "%s (id=%s - loop)", cons->rsc_lh->id, cons->id);
             continue;
         }
 
         hdr = colocations_header(cons->rsc_lh, cons, TRUE);
         out->list_item(out, NULL, "%s", hdr);
         free(hdr);
 
         /* Empty list header just for indentation of information about this resource. */
         out->begin_list(out, NULL, NULL, NULL);
 
         out->message(out, "locations-list", cons->rsc_lh);
         if (recursive) {
             out->message(out, "rscs-colocated-with-list", cons->rsc_lh, recursive);
         }
 
         out->end_list(out);
     }
 
     PCMK__OUTPUT_LIST_FOOTER(out, rc);
     return rc;
 }
 
 PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pe_resource_t *", "gboolean")
 static int
 rscs_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     gboolean recursive = va_arg(args, gboolean);
 
     int rc = pcmk_rc_no_output;
 
     if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
         return rc;
     }
 
     pe__set_resource_flags(rsc, pe_rsc_allocating);
     for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
         pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
 
         if (pcmk_is_set(cons->rsc_lh->flags, pe_rsc_allocating)) {
             colocations_xml_node(out, cons->rsc_lh, cons);
             continue;
         }
 
         colocations_xml_node(out, cons->rsc_lh, cons);
         do_locations_list_xml(out, cons->rsc_lh, false);
 
         if (recursive) {
             out->message(out, "rscs-colocated-with-list", cons->rsc_lh, recursive);
         }
     }
 
     return rc;
 }
 
 PCMK__OUTPUT_ARGS("locations-list", "pe_resource_t *")
 static int
 locations_list(pcmk__output_t *out, va_list args) {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
 
     GList *lpc = NULL;
     GList *list = rsc->rsc_location;
     int rc = pcmk_rc_no_output;
 
     for (lpc = list; lpc != NULL; lpc = lpc->next) {
         pe__location_t *cons = lpc->data;
 
         GList *lpc2 = NULL;
 
         for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) {
             pe_node_t *node = (pe_node_t *) lpc2->data;
             char *score = score2char(node->weight);
 
             PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Locations");
             out->list_item(out, NULL, "Node %s (score=%s, id=%s, rsc=%s)",
                            node->details->uname, score, cons->id, rsc->id);
             free(score);
         }
     }
 
     PCMK__OUTPUT_LIST_FOOTER(out, rc);
     return rc;
 }
 
 PCMK__OUTPUT_ARGS("locations-list", "pe_resource_t *")
 static int
 locations_list_xml(pcmk__output_t *out, va_list args) {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     return do_locations_list_xml(out, rsc, true);
 }
 
 PCMK__OUTPUT_ARGS("stacks-constraints", "pe_resource_t *", "pe_working_set_t *", "gboolean")
 static int
 stacks_and_constraints(pcmk__output_t *out, va_list args) {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
     gboolean recursive = va_arg(args, gboolean);
 
     xmlNodePtr cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS,
                                                  data_set->input);
 
     unpack_constraints(cib_constraints, data_set);
 
     // Constraints apply to group/clone, not member/instance
     rsc = uber_parent(rsc);
 
     out->message(out, "locations-list", rsc);
 
     pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
     out->message(out, "rscs-colocated-with-list", rsc, recursive);
 
     pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
     out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("stacks-constraints", "pe_resource_t *", "pe_working_set_t *", "gboolean")
 static int
 stacks_and_constraints_xml(pcmk__output_t *out, va_list args) {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
     gboolean recursive = va_arg(args, gboolean);
 
     xmlNodePtr cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS,
                                                  data_set->input);
 
     unpack_constraints(cib_constraints, data_set);
 
     // Constraints apply to group/clone, not member/instance
     rsc = uber_parent(rsc);
 
     pcmk__output_xml_create_parent(out, "constraints", NULL);
     do_locations_list_xml(out, rsc, false);
 
     pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
     out->message(out, "rscs-colocated-with-list", rsc, recursive);
 
     pe__clear_resource_flags_on_all(data_set, pe_rsc_allocating);
     out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
 
     pcmk__output_xml_pop_parent(out);
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *", "const char *")
 static int
 health_text(pcmk__output_t *out, va_list args)
 {
     const char *sys_from = va_arg(args, const char *);
     const char *host_from = va_arg(args, const char *);
     const char *fsa_state = va_arg(args, const char *);
     const char *result = va_arg(args, const char *);
 
     if (!out->is_quiet(out)) {
         out->info(out, "Status of %s@%s: %s (%s)", crm_str(sys_from),
                        crm_str(host_from), crm_str(fsa_state), crm_str(result));
     } else if (fsa_state != NULL) {
         out->info(out, "%s", fsa_state);
     }
 
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *", "const char *")
 static int
 health_xml(pcmk__output_t *out, va_list args)
 {
     const char *sys_from = va_arg(args, const char *);
     const char *host_from = va_arg(args, const char *);
     const char *fsa_state = va_arg(args, const char *);
     const char *result = va_arg(args, const char *);
 
     pcmk__output_create_xml_node(out, crm_str(sys_from),
                                  "node_name", crm_str(host_from),
                                  "state", crm_str(fsa_state),
                                  "result", crm_str(result),
                                  NULL);
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *", "const char *", "const char *")
 static int
 pacemakerd_health_text(pcmk__output_t *out, va_list args)
 {
     const char *sys_from = va_arg(args, const char *);
     const char *state = va_arg(args, const char *);
     const char *last_updated = va_arg(args, const char *);
 
     if (!out->is_quiet(out)) {
         out->info(out, "Status of %s: '%s' %s %s", crm_str(sys_from),
                   crm_str(state), (!pcmk__str_empty(last_updated))?
                   "last updated":"", crm_str(last_updated));
     } else {
         out->info(out, "%s", crm_str(state));
     }
 
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *", "const char *", "const char *")
 static int
 pacemakerd_health_xml(pcmk__output_t *out, va_list args)
 {
     const char *sys_from = va_arg(args, const char *);
     const char *state = va_arg(args, const char *);
     const char *last_updated = va_arg(args, const char *);
 
     pcmk__output_create_xml_node(out, crm_str(sys_from),
                                  "state", crm_str(state),
                                  "last_updated", crm_str(last_updated),
                                  NULL);
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("dc", "const char *")
 static int
 dc_text(pcmk__output_t *out, va_list args)
 {
     const char *dc = va_arg(args, const char *);
 
     if (!out->is_quiet(out)) {
         out->info(out, "Designated Controller is: %s", crm_str(dc));
     } else if (dc != NULL) {
         out->info(out, "%s", dc);
     }
 
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("dc", "const char *")
 static int
 dc_xml(pcmk__output_t *out, va_list args)
 {
     const char *dc = va_arg(args, const char *);
 
     pcmk__output_create_xml_node(out, "dc",
                                  "node_name", crm_str(dc),
                                  NULL);
     return pcmk_rc_ok;
 }
 
-
-PCMK__OUTPUT_ARGS("crmadmin-node-list", "xmlNodePtr", "gboolean")
-static int
-crmadmin_node_list(pcmk__output_t *out, va_list args)
-{
-    xmlNodePtr xml_node = va_arg(args, xmlNodePtr);
-    gboolean BASH_EXPORT = va_arg(args, gboolean);
-
-    int found = 0;
-    xmlNode *node = NULL;
-    xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node);
-
-    out->begin_list(out, NULL, NULL, "nodes");
-
-    for (node = first_named_child(nodes, XML_CIB_TAG_NODE); node != NULL;
-         node = crm_next_same_xml(node)) {
-        const char *node_type = BASH_EXPORT ? NULL :
-                     crm_element_value(node, XML_ATTR_TYPE);
-        out->message(out, "crmadmin-node", node_type,
-                     crm_str(crm_element_value(node, XML_ATTR_UNAME)),
-                     crm_str(crm_element_value(node, XML_ATTR_ID)),
-                     BASH_EXPORT);
-
-        found++;
-    }
-    // @TODO List Pacemaker Remote nodes that don't have a <node> entry
-
-    out->end_list(out);
-
-    if (found == 0) {
-        out->info(out, "No nodes configured");
-    }
-
-    return pcmk_rc_ok;
-}
-
 PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *", "const char *", "gboolean")
 static int
 crmadmin_node_text(pcmk__output_t *out, va_list args)
 {
     const char *type = va_arg(args, const char *);
     const char *name = va_arg(args, const char *);
     const char *id = va_arg(args, const char *);
     gboolean BASH_EXPORT = va_arg(args, gboolean);
 
-    if (BASH_EXPORT) {
+    if (out->is_quiet(out)) {
+        out->info(out, "%s", crm_str(name));
+    } else if (BASH_EXPORT) {
         out->info(out, "export %s=%s", crm_str(name), crm_str(id));
     } else {
         out->info(out, "%s node: %s (%s)", type ? type : "member",
                   crm_str(name), crm_str(id));
     }
 
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *", "const char *", "gboolean")
 static int
 crmadmin_node_xml(pcmk__output_t *out, va_list args)
 {
     const char *type = va_arg(args, const char *);
     const char *name = va_arg(args, const char *);
     const char *id = va_arg(args, const char *);
 
     pcmk__output_create_xml_node(out, "node",
                                  "type", type ? type : "member",
                                  "name", crm_str(name),
                                  "id", crm_str(id),
                                  NULL);
     return pcmk_rc_ok;
 }
 
 PCMK__OUTPUT_ARGS("digests", "pe_resource_t *", "pe_node_t *", "const char *",
                   "guint", "op_digest_cache_t *")
 static int
 digests_text(pcmk__output_t *out, va_list args)
 {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     pe_node_t *node = va_arg(args, pe_node_t *);
     const char *task = va_arg(args, const char *);
     guint interval_ms = va_arg(args, guint);
     op_digest_cache_t *digests = va_arg(args, op_digest_cache_t *);
 
     char *action_desc = NULL;
     const char *rsc_desc = "unknown resource";
     const char *node_desc = "unknown node";
 
     if (interval_ms != 0) {
         action_desc = crm_strdup_printf("%ums-interval %s action", interval_ms,
                                         ((task == NULL)? "unknown" : task));
     } else if (pcmk__str_eq(task, "monitor", pcmk__str_none)) {
         action_desc = strdup("probe action");
     } else {
         action_desc = crm_strdup_printf("%s action",
                                         ((task == NULL)? "unknown" : task));
     }
     if ((rsc != NULL) && (rsc->id != NULL)) {
         rsc_desc = rsc->id;
     }
     if ((node != NULL) && (node->details->uname != NULL)) {
         node_desc = node->details->uname;
     }
     out->begin_list(out, NULL, NULL, "Digests for %s %s on %s",
                     rsc_desc, action_desc, node_desc);
     free(action_desc);
 
     if (digests == NULL) {
         out->list_item(out, NULL, "none");
         out->end_list(out);
         return pcmk_rc_ok;
     }
     if (digests->digest_all_calc != NULL) {
         out->list_item(out, NULL, "%s (all parameters)",
                        digests->digest_all_calc);
     }
     if (digests->digest_secure_calc != NULL) {
         out->list_item(out, NULL, "%s (non-private parameters)",
                        digests->digest_secure_calc);
     }
     if (digests->digest_restart_calc != NULL) {
         out->list_item(out, NULL, "%s (non-reloadable parameters)",
                        digests->digest_restart_calc);
     }
     out->end_list(out);
     return pcmk_rc_ok;
 }
 
 static void
 add_digest_xml(xmlNode *parent, const char *type, const char *digest,
                xmlNode *digest_source)
 {
     if (digest != NULL) {
         xmlNodePtr digest_xml = create_xml_node(parent, "digest");
 
         crm_xml_add(digest_xml, "type", ((type == NULL)? "unspecified" : type));
         crm_xml_add(digest_xml, "hash", digest);
         if (digest_source != NULL) {
             add_node_copy(digest_xml, digest_source);
         }
     }
 }
 
 PCMK__OUTPUT_ARGS("digests", "pe_resource_t *", "pe_node_t *", "const char *",
                   "guint", "op_digest_cache_t *")
 static int
 digests_xml(pcmk__output_t *out, va_list args)
 {
     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
     pe_node_t *node = va_arg(args, pe_node_t *);
     const char *task = va_arg(args, const char *);
     guint interval_ms = va_arg(args, guint);
     op_digest_cache_t *digests = va_arg(args, op_digest_cache_t *);
 
     char *interval_s = crm_strdup_printf("%ums", interval_ms);
     xmlNode *xml = NULL;
 
     xml = pcmk__output_create_xml_node(out, "digests",
                                        "resource", crm_str(rsc->id),
                                        "node", crm_str(node->details->uname),
                                        "task", crm_str(task),
                                        "interval", interval_s,
                                        NULL);
     free(interval_s);
     if (digests != NULL) {
         add_digest_xml(xml, "all", digests->digest_all_calc,
                        digests->params_all);
         add_digest_xml(xml, "nonprivate", digests->digest_secure_calc,
                        digests->params_secure);
         add_digest_xml(xml, "nonreloadable", digests->digest_restart_calc,
                        digests->params_restart);
     }
     return pcmk_rc_ok;
 }
 
 static pcmk__message_entry_t fmt_functions[] = {
     { "rsc-is-colocated-with-list", "default", rsc_is_colocated_with_list },
     { "rsc-is-colocated-with-list", "xml", rsc_is_colocated_with_list_xml },
     { "rscs-colocated-with-list", "default", rscs_colocated_with_list },
     { "rscs-colocated-with-list", "xml", rscs_colocated_with_list_xml },
     { "locations-list", "default", locations_list },
     { "locations-list", "xml", locations_list_xml },
     { "stacks-constraints", "default", stacks_and_constraints },
     { "stacks-constraints", "xml", stacks_and_constraints_xml },
     { "health", "default", health_text },
     { "health", "xml", health_xml },
     { "pacemakerd-health", "default", pacemakerd_health_text },
     { "pacemakerd-health", "xml", pacemakerd_health_xml },
     { "dc", "default", dc_text },
     { "dc", "xml", dc_xml },
-    { "crmadmin-node-list", "default", crmadmin_node_list },
     { "crmadmin-node", "default", crmadmin_node_text },
     { "crmadmin-node", "xml", crmadmin_node_xml },
     { "digests", "default", digests_text },
     { "digests", "xml", digests_xml },
 
     { NULL, NULL, NULL }
 };
 
 void
 pcmk__register_lib_messages(pcmk__output_t *out) {
     pcmk__register_messages(out, fmt_functions);
 }