From 132559777bb2f885bfbe0f063610f3b520cf5429 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mar 09 2021 06:18:39 +0000 Subject: Feature: tools: Use formatted output for stacks and constraints. This also changes the format for text output. The new output uses the underlying text list code for handling indentation and nesting, while the old code passed around an indentation prefix. However, this does mean we end up with a little of the text list fanciness as well. Hopefully that will not be a problem. --- diff --git a/cts/cli/regression.tools.exp b/cts/cli/regression.tools.exp index 7166714..221730d 100644 --- a/cts/cli/regression.tools.exp +++ b/cts/cli/regression.tools.exp @@ -2001,12 +2001,13 @@ WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score =#=#=#= End test: Ban dummy from node1 - OK (0) =#=#=#= * Passed: crm_resource - Ban dummy from node1 =#=#=#= Begin test: Show where a resource is running =#=#=#= -resource dummy is running on: node1 +resource dummy is running on: node1 =#=#=#= End test: Show where a resource is running - OK (0) =#=#=#= * Passed: crm_resource - Show where a resource is running =#=#=#= Begin test: Show constraints on a resource =#=#=#= -* dummy - : Node node1 (score=-INFINITY, id=cli-ban-dummy-on-node1) +dummy: + * Locations: + * Node node1 (score=-INFINITY, id=cli-ban-dummy-on-node1) =#=#=#= End test: Show constraints on a resource - OK (0) =#=#=#= * Passed: crm_resource - Show constraints on a resource =#=#=#= Begin test: Ban dummy from node2 =#=#=#= diff --git a/include/pcmki/pcmki_output.h b/include/pcmki/pcmki_output.h index 2b750fb..0faef35 100644 --- a/include/pcmki/pcmki_output.h +++ b/include/pcmki/pcmki_output.h @@ -21,6 +21,13 @@ extern pcmk__supported_format_t pcmk__out_formats[]; int pcmk__out_prologue(pcmk__output_t **out, xmlNodePtr *xml); void pcmk__out_epilogue(pcmk__output_t *out, xmlNodePtr *xml, int retval); +/* This function registers only the formatted output messages that are a part + * of libpacemaker. It is not to be confused with pcmk__register_messages, + * which is a part of formatted output support and registers a whole table of + * messages at a time. + */ +void pcmk__register_lib_messages(pcmk__output_t *out); + #ifdef __cplusplus } #endif diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c index adf4c34..306e561 100644 --- a/lib/pacemaker/pcmk_output.c +++ b/lib/pacemaker/pcmk_output.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include pcmk__supported_format_t pcmk__out_formats[] = { PCMK__SUPPORTED_FORMAT_XML, @@ -46,3 +46,329 @@ pcmk__out_epilogue(pcmk__output_t *out, xmlNodePtr *xml, int retval) { pcmk__output_free(out); } + +PCMK__OUTPUT_ARGS("colocations-list", "pe_resource_t *", "gboolean", "gboolean") +static int colocations_list(pcmk__output_t *out, va_list args) { + pe_resource_t *rsc = va_arg(args, pe_resource_t *); + gboolean dependents = va_arg(args, gboolean); + gboolean recursive = va_arg(args, gboolean); + + GListPtr lpc = NULL; + GListPtr list = rsc->rsc_cons; + bool printed_header = false; + + if (dependents) { + list = rsc->rsc_cons_lhs; + } + + if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) { + return pcmk_rc_no_output; + } + + pe__set_resource_flags(rsc, pe_rsc_allocating); + for (lpc = list; lpc != NULL; lpc = lpc->next) { + rsc_colocation_t *cons = (rsc_colocation_t *) lpc->data; + + char *score = NULL; + pe_resource_t *peer = cons->rsc_rh; + + if (dependents) { + peer = cons->rsc_lh; + } + + if (pcmk_is_set(peer->flags, pe_rsc_allocating)) { + if (dependents == FALSE) { + if (!printed_header) { + out->begin_list(out, NULL, NULL, "Colocations"); + printed_header = true; + } + + out->list_item(out, NULL, "%s (id=%s - loop)", peer->id, cons->id); + } + continue; + } + + if (dependents && recursive) { + if (!printed_header) { + out->begin_list(out, NULL, NULL, "Colocations"); + printed_header = true; + } + + out->message(out, "colocations-list", rsc, dependents, recursive); + } + + if (!printed_header) { + out->begin_list(out, NULL, NULL, "Colocations"); + printed_header = true; + } + + score = score2char(cons->score); + if (cons->role_rh > RSC_ROLE_STARTED) { + out->list_item(out, NULL, "%s (score=%s, %s role=%s, id=%s", + peer->id, score, dependents ? "needs" : "with", + role2text(cons->role_rh), cons->id); + } else { + out->list_item(out, NULL, "%s (score=%s, id=%s", + peer->id, score, cons->id); + } + + free(score); + out->message(out, "locations-list", peer); + + if (!dependents && recursive) { + out->message(out, "colocations-list", rsc, dependents, recursive); + } + } + + if (printed_header) { + out->end_list(out); + } + + return pcmk_rc_no_output; +} + +PCMK__OUTPUT_ARGS("colocations-list", "pe_resource_t *", "gboolean", "gboolean") +static int colocations_list_xml(pcmk__output_t *out, va_list args) { + pe_resource_t *rsc = va_arg(args, pe_resource_t *); + gboolean dependents = va_arg(args, gboolean); + gboolean recursive = va_arg(args, gboolean); + + GListPtr lpc = NULL; + GListPtr list = rsc->rsc_cons; + bool printed_header = false; + + if (dependents) { + list = rsc->rsc_cons_lhs; + } + + if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) { + return pcmk_rc_ok; + } + + pe__set_resource_flags(rsc, pe_rsc_allocating); + for (lpc = list; lpc != NULL; lpc = lpc->next) { + rsc_colocation_t *cons = (rsc_colocation_t *) lpc->data; + pe_resource_t *peer = cons->rsc_rh; + char *score = NULL; + + if (dependents) { + peer = cons->rsc_lh; + } + + if (pcmk_is_set(peer->flags, pe_rsc_allocating)) { + if (dependents == FALSE) { + xmlNodePtr node; + + if (!printed_header) { + pcmk__output_xml_create_parent(out, "colocations"); + printed_header = true; + } + + node = pcmk__output_create_xml_node(out, "colocation"); + xmlSetProp(node, (pcmkXmlStr) "peer", (pcmkXmlStr) peer->id); + xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) cons->id); + } + continue; + } + + if (dependents && recursive) { + if (!printed_header) { + pcmk__output_xml_create_parent(out, "colocations"); + printed_header = true; + } + + out->message(out, "colocations-list", rsc, dependents, recursive); + } + + if (!printed_header) { + pcmk__output_xml_create_parent(out, "colocations"); + printed_header = true; + } + + score = score2char(cons->score); + if (cons->role_rh > RSC_ROLE_STARTED) { + xmlNodePtr node = pcmk__output_create_xml_node(out, "colocation"); + xmlSetProp(node, (pcmkXmlStr) "peer", (pcmkXmlStr) peer->id); + xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) cons->id); + xmlSetProp(node, (pcmkXmlStr) "score", (pcmkXmlStr) score); + xmlSetProp(node, (pcmkXmlStr) "dependents", + (pcmkXmlStr) (dependents ? "needs" : "with")); + xmlSetProp(node, (pcmkXmlStr) "role", (pcmkXmlStr) role2text(cons->role_rh)); + } else { + xmlNodePtr node = pcmk__output_create_xml_node(out, "colocation"); + xmlSetProp(node, (pcmkXmlStr) "peer", (pcmkXmlStr) peer->id); + xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) cons->id); + xmlSetProp(node, (pcmkXmlStr) "score", (pcmkXmlStr) score); + } + + free(score); + out->message(out, "locations-list", peer); + + if (!dependents && recursive) { + out->message(out, "colocations-list", rsc, dependents, recursive); + } + } + + if (printed_header) { + pcmk__output_xml_pop_parent(out); + } + + return pcmk_rc_ok; +} + +PCMK__OUTPUT_ARGS("locations-list", "pe_resource_t *") +static int locations_list(pcmk__output_t *out, va_list args) { + pe_resource_t *rsc G_GNUC_UNUSED = va_arg(args, pe_resource_t *); + + GListPtr lpc = NULL; + GListPtr list = rsc->rsc_location; + + out->begin_list(out, NULL, NULL, "Locations"); + + for (lpc = list; lpc != NULL; lpc = lpc->next) { + pe__location_t *cons = lpc->data; + + GListPtr 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); + + out->list_item(out, NULL, "Node %s (score=%s, id=%s)", + node->details->uname, score, cons->id); + free(score); + } + } + + out->end_list(out); + + return pcmk_rc_ok; +} + +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 *); + + GListPtr lpc = NULL; + GListPtr list = rsc->rsc_location; + + pcmk__output_xml_create_parent(out, "locations"); + + for (lpc = list; lpc != NULL; lpc = lpc->next) { + pe__location_t *cons = lpc->data; + + GListPtr 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); + + xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "location"); + xmlSetProp(xml_node, (pcmkXmlStr) "host", (pcmkXmlStr) node->details->uname); + xmlSetProp(xml_node, (pcmkXmlStr) "id", (pcmkXmlStr) cons->id); + xmlSetProp(xml_node, (pcmkXmlStr) "score", (pcmkXmlStr) score); + + free(score); + } + } + + pcmk__output_xml_pop_parent(out); + + return pcmk_rc_ok; +} + +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 G_GNUC_UNUSED = va_arg(args, pe_resource_t *); + pe_working_set_t *data_set G_GNUC_UNUSED = va_arg(args, pe_working_set_t *); + gboolean recursive G_GNUC_UNUSED = va_arg(args, gboolean); + + GListPtr lpc = NULL; + xmlNode *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); + + for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { + pe_resource_t *r = (pe_resource_t *) lpc->data; + + pe__clear_resource_flags(r, pe_rsc_allocating); + } + + out->message(out, "colocations-list", rsc, TRUE, recursive); + + out->begin_list(out, NULL, NULL, "%s", rsc->id); + out->message(out, "locations-list", rsc); + out->end_list(out); + + for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { + pe_resource_t *r = (pe_resource_t *) lpc->data; + + pe__clear_resource_flags(r, pe_rsc_allocating); + } + + out->message(out, "colocations-list", rsc, FALSE, 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); + + GListPtr lpc = NULL; + xmlNodePtr node = NULL; + xmlNode *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); + + for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { + pe_resource_t *r = (pe_resource_t *) lpc->data; + + pe__clear_resource_flags(r, pe_rsc_allocating); + } + + pcmk__output_xml_create_parent(out, "constraints"); + + out->message(out, "colocations-list", rsc, TRUE, recursive); + + node = pcmk__output_xml_create_parent(out, "resource"); + xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) rsc->id); + out->message(out, "locations-list", rsc); + pcmk__output_xml_pop_parent(out); + + for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { + pe_resource_t *r = (pe_resource_t *) lpc->data; + + pe__clear_resource_flags(r, pe_rsc_allocating); + } + + out->message(out, "colocations-list", rsc, FALSE, recursive); + return pcmk_rc_ok; +} + +static pcmk__message_entry_t fmt_functions[] = { + { "colocations-list", "default", colocations_list }, + { "colocations-list", "xml", colocations_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 }, + + { NULL, NULL, NULL } +}; + +void +pcmk__register_lib_messages(pcmk__output_t *out) { + pcmk__register_messages(out, fmt_functions); +} diff --git a/tools/crm_resource.c b/tools/crm_resource.c index 9acb955..bfa74eb 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -1194,38 +1194,6 @@ list_providers(pcmk__output_t *out, const char *agent_spec, crm_exit_t *exit_cod return rc; } -static void -list_stacks_and_constraints(pcmk__output_t *out, pe_resource_t *rsc, bool recursive) -{ - GListPtr lpc = NULL; - xmlNode *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); - - for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { - pe_resource_t *r = (pe_resource_t *) lpc->data; - - pe__clear_resource_flags(r, pe_rsc_allocating); - } - - cli_resource_print_colocation(out, rsc, TRUE, recursive, 1); - - fprintf(stdout, "* %s\n", rsc->id); - cli_resource_print_location(out, rsc, NULL); - - for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { - pe_resource_t *r = (pe_resource_t *) lpc->data; - - pe__clear_resource_flags(r, pe_rsc_allocating); - } - - cli_resource_print_colocation(out, rsc, FALSE, recursive, 1); -} - static int populate_working_set(xmlNodePtr *cib_xml_copy) { @@ -1629,7 +1597,8 @@ main(int argc, char **argv) pcmk__force_args(context, &error, "%s --xml-substitute", g_get_prgname()); } } else if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches)) { - if (options.rsc_cmd == cmd_list_resources) { + if (options.rsc_cmd == cmd_colocations || options.rsc_cmd == cmd_colocations_deep || + options.rsc_cmd == cmd_list_resources) { pcmk__force_args(context, &error, "%s --text-fancy", g_get_prgname()); } } @@ -1637,6 +1606,7 @@ main(int argc, char **argv) pe__register_messages(out); crm_resource_register_messages(out); lrmd__register_messages(out); + pcmk__register_lib_messages(out); if (args->version) { out->version(out, false); @@ -1804,11 +1774,11 @@ main(int argc, char **argv) break; case cmd_colocations: - list_stacks_and_constraints(out, rsc, false); + rc = out->message(out, "stacks-constraints", rsc, data_set, false); break; case cmd_colocations_deep: - list_stacks_and_constraints(out, rsc, true); + rc = out->message(out, "stacks-constraints", rsc, data_set, true); break; case cmd_cts: diff --git a/tools/crm_resource.h b/tools/crm_resource.h index 6de2457..5bfadb7 100644 --- a/tools/crm_resource.h +++ b/tools/crm_resource.h @@ -53,10 +53,6 @@ int cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, int cib_optio void cli_resource_print_cts(pcmk__output_t *out, pe_resource_t * rsc); void cli_resource_print_raw(pcmk__output_t *out, pe_resource_t * rsc); void cli_resource_print_cts_constraints(pcmk__output_t *out, pe_working_set_t * data_set); -void cli_resource_print_location(pcmk__output_t *out, pe_resource_t * rsc, - const char *prefix); -void cli_resource_print_colocation(pcmk__output_t *out, pe_resource_t * rsc, - bool dependents, bool recursive, int offset); int cli_resource_print(pcmk__output_t *out, pe_resource_t *rsc, pe_working_set_t *data_set, bool expanded); diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c index 5ff3e9b..6303863 100644 --- a/tools/crm_resource_print.c +++ b/tools/crm_resource_print.c @@ -108,97 +108,6 @@ cli_resource_print_operations(pcmk__output_t *out, const char *rsc_id, return rc; } -void -cli_resource_print_location(pcmk__output_t *out, pe_resource_t * rsc, const char *prefix) -{ - GListPtr lpc = NULL; - GListPtr list = rsc->rsc_location; - int offset = 0; - - if (prefix) { - offset = strlen(prefix) - 2; - } - - for (lpc = list; lpc != NULL; lpc = lpc->next) { - pe__location_t *cons = lpc->data; - - GListPtr 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); - - fprintf(stdout, "%s: Node %-*s (score=%s, id=%s)\n", - prefix ? prefix : " ", 71 - offset, node->details->uname, score, cons->id); - free(score); - } - } -} - -void -cli_resource_print_colocation(pcmk__output_t *out, pe_resource_t * rsc, - bool dependents, bool recursive, int offset) -{ - char *prefix = NULL; - GListPtr lpc = NULL; - GListPtr list = rsc->rsc_cons; - - prefix = calloc(1, (offset * 4) + 1); - memset(prefix, ' ', offset * 4); - - if (dependents) { - list = rsc->rsc_cons_lhs; - } - - if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) { - /* Break colocation loops */ - printf("loop %s\n", rsc->id); - free(prefix); - return; - } - - pe__set_resource_flags(rsc, pe_rsc_allocating); - for (lpc = list; lpc != NULL; lpc = lpc->next) { - rsc_colocation_t *cons = (rsc_colocation_t *) lpc->data; - - char *score = NULL; - pe_resource_t *peer = cons->rsc_rh; - - if (dependents) { - peer = cons->rsc_lh; - } - - if (pcmk_is_set(peer->flags, pe_rsc_allocating)) { - if (dependents == FALSE) { - fprintf(stdout, "%s%-*s (id=%s - loop)\n", prefix, 80 - (4 * offset), peer->id, - cons->id); - } - continue; - } - - if (dependents && recursive) { - cli_resource_print_colocation(out, peer, dependents, recursive, offset + 1); - } - - score = score2char(cons->score); - if (cons->role_rh > RSC_ROLE_STARTED) { - fprintf(stdout, "%s%-*s (score=%s, %s role=%s, id=%s)\n", prefix, 80 - (4 * offset), - peer->id, score, dependents ? "needs" : "with", role2text(cons->role_rh), - cons->id); - } else { - fprintf(stdout, "%s%-*s (score=%s, id=%s)\n", prefix, 80 - (4 * offset), - peer->id, score, cons->id); - } - cli_resource_print_location(out, peer, prefix); - free(score); - - if (!dependents && recursive) { - cli_resource_print_colocation(out, peer, dependents, recursive, offset + 1); - } - } - free(prefix); -} - // \return Standard Pacemaker return code int cli_resource_print(pcmk__output_t *out, pe_resource_t *rsc,