From 562cd20d76810670b4efe9a535784c2c6a165bc4 Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Feb 24 2021 16:08:54 +0000 Subject: API: libpacemaker: new API pcmk_resource_digests() --- diff --git a/include/pacemaker.h b/include/pacemaker.h index b2a73cd..51bf585 100644 --- a/include/pacemaker.h +++ b/include/pacemaker.h @@ -1,5 +1,5 @@ /* - * Copyright 2019 the Pacemaker project contributors + * Copyright 2019-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -20,8 +20,11 @@ extern "C" { * \ingroup pacemaker */ -# include +# include # include +# include + +# include /*! * \brief Get controller status @@ -55,6 +58,21 @@ int pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms) */ int pcmk_pacemakerd_status(xmlNodePtr *xml, char *ipc_name, unsigned int message_timeout_ms); +/*! + * \brief Calculate and output resource operation digests + * + * \param[out] xml Where to store XML with result + * \param[in] rsc Resource to calculate digests for + * \param[in] node Node whose operation history should be used + * \param[in] overrides Hash table of configuration parameters to override + * \param[in] data_set Cluster working set (with status) + * + * \return Standard Pacemaker return code + */ +int pcmk_resource_digests(xmlNodePtr *xml, pe_resource_t *rsc, + pe_node_t *node, GHashTable *overrides, + pe_working_set_t *data_set); + #ifdef BUILD_PUBLIC_LIBPACEMAKER /*! diff --git a/include/pcmki/pcmki_resource.h b/include/pcmki/pcmki_resource.h index effa945..9d2afb5 100644 --- a/include/pcmki/pcmki_resource.h +++ b/include/pcmki/pcmki_resource.h @@ -9,6 +9,13 @@ #ifndef PCMKI_RESOURCE__H #define PCMKI_RESOURCE__H +#include + #include +#include + +int pcmk__resource_digests(pcmk__output_t *out, pe_resource_t *rsc, + pe_node_t *node, GHashTable *overrides, + pe_working_set_t *data_set); #endif /* PCMK_RESOURCE__H */ diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c index 500afd1..bc4b91a 100644 --- a/lib/pacemaker/pcmk_output.c +++ b/lib/pacemaker/pcmk_output.c @@ -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. * @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -539,6 +540,108 @@ crmadmin_node_xml(pcmk__output_t *out, va_list args) 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 }, @@ -557,6 +660,8 @@ static pcmk__message_entry_t fmt_functions[] = { { "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 } }; diff --git a/lib/pacemaker/pcmk_resource.c b/lib/pacemaker/pcmk_resource.c index 05614fc..197edf8 100644 --- a/lib/pacemaker/pcmk_resource.c +++ b/lib/pacemaker/pcmk_resource.c @@ -9,6 +9,7 @@ #include +#include #include #include @@ -19,3 +20,121 @@ #include #include + +// Search path for resource operation history (takes node name and resource ID) +#define XPATH_OP_HISTORY "//" XML_CIB_TAG_STATUS \ + "/" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" \ + "/" XML_CIB_TAG_LRM "/" XML_LRM_TAG_RESOURCES \ + "/" XML_LRM_TAG_RESOURCE "[@" XML_ATTR_ID "='%s']" + +static xmlNode * +best_op(pe_resource_t *rsc, pe_node_t *node, pe_working_set_t *data_set) +{ + char *xpath = NULL; + xmlNode *history = NULL; + xmlNode *best = NULL; + + // Find node's resource history + xpath = crm_strdup_printf(XPATH_OP_HISTORY, node->details->uname, rsc->id); + history = get_xpath_object(xpath, data_set->input, LOG_NEVER); + free(xpath); + + // Examine each history entry + for (xmlNode *lrm_rsc_op = first_named_child(history, XML_LRM_TAG_RSC_OP); + lrm_rsc_op != NULL; lrm_rsc_op = crm_next_same_xml(lrm_rsc_op)) { + + const char *digest = crm_element_value(lrm_rsc_op, + XML_LRM_ATTR_RESTART_DIGEST); + guint interval_ms = 0; + + crm_element_value_ms(lrm_rsc_op, XML_LRM_ATTR_INTERVAL, &interval_ms); + + if (pcmk__ends_with(ID(lrm_rsc_op), "_last_failure_0") + || (interval_ms != 0)) { + + // Only use last failure or recurring op if nothing else available + if (best == NULL) { + best = lrm_rsc_op; + } + continue; + } + + best = lrm_rsc_op; + if (digest != NULL) { + // Any non-recurring action with a restart digest is sufficient + break; + } + } + return best; +} + +/*! + * \internal + * \brief Calculate and output resource operation digests + * + * \param[in] out Output object + * \param[in] rsc Resource to calculate digests for + * \param[in] node Node whose operation history should be used + * \param[in] overrides Hash table of configuration parameters to override + * \param[in] data_set Cluster working set (with status) + * + * \return Standard Pacemaker return code + */ +int +pcmk__resource_digests(pcmk__output_t *out, pe_resource_t *rsc, + pe_node_t *node, GHashTable *overrides, + pe_working_set_t *data_set) +{ + const char *task = NULL; + xmlNode *xml_op = NULL; + op_digest_cache_t *digests = NULL; + guint interval_ms = 0; + int rc = pcmk_rc_ok; + + if ((out == NULL) || (rsc == NULL) || (node == NULL) || (data_set == NULL)) { + return EINVAL; + } + if (rsc->variant != pe_native) { + // Only primitives get operation digests + return EOPNOTSUPP; + } + + // Find XML of operation history to use + xml_op = best_op(rsc, node, data_set); + + // Generate an operation key + if (xml_op != NULL) { + task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); + } + if (task == NULL) { // Assume start if no history is available + task = RSC_START; + interval_ms = 0; + } + + // Calculate and show digests + digests = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op, + overrides, true, data_set); + rc = out->message(out, "digests", rsc, node, task, interval_ms, digests); + + pe__free_digests(digests); + return rc; +} + +int +pcmk_resource_digests(xmlNodePtr *xml, pe_resource_t *rsc, + pe_node_t *node, GHashTable *overrides, + pe_working_set_t *data_set) +{ + 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__resource_digests(out, rsc, node, overrides, data_set); + pcmk__out_epilogue(out, xml, rc); + return rc; +} diff --git a/xml/Makefile.am b/xml/Makefile.am index a56258d..ac906bb 100644 --- a/xml/Makefile.am +++ b/xml/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2004-2019 the Pacemaker project contributors +# Copyright 2004-2021 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -50,7 +50,13 @@ version_pairs_last = $(wordlist \ # problems. # Names of API schemas that form the choices for pacemaker-result content -API_request_base = command-output crm_mon crm_resource crmadmin stonith_admin version +API_request_base = command-output \ + crm_mon \ + crm_resource \ + crmadmin \ + digests \ + stonith_admin \ + version # Names of CIB schemas that form the choices for cib/configuration content CIB_cfg_base = options nodes resources constraints fencing acls tags alerts diff --git a/xml/api/digests-2.6.rng b/xml/api/digests-2.6.rng new file mode 100644 index 0000000..7e843d4 --- /dev/null +++ b/xml/api/digests-2.6.rng @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +