From 591324bc6f15c12bf1547c7c20e8e99da219f1fc Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Thu, 22 Oct 2020 18:06:26 -0500 Subject: [PATCH 1/5] Refactor: libpe_status: functionize creating operation digests better Basically, this takes most of rsc_action_digest(), and puts it in a new, exposed function pe__calculate_digests(), for future reuse. The exposed function creates a new op_digest_cache_t object with calculated digests; rsc_action_digests() takes that and puts it in a node's digest cache. This additionally functionizes most of pe__calculate_digests(), with separate functions for creating each of the three digests that go into a digest object: the digest of all parameters, the digest of non-private parameters, and the digest of reload parameters. There are no changes in how the code works. --- include/crm/pengine/internal.h | 5 + lib/pengine/utils.c | 304 +++++++++++++++++++++++++++-------------- 2 files changed, 208 insertions(+), 101 deletions(-) diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h index 396d707..7f22512 100644 --- a/include/crm/pengine/internal.h +++ b/include/crm/pengine/internal.h @@ -528,6 +528,11 @@ typedef struct op_digest_cache_s { char *digest_restart_calc; } op_digest_cache_t; +op_digest_cache_t *pe__calculate_digests(pe_resource_t *rsc, const char *task, + const char *key, pe_node_t *node, + xmlNode *xml_op, bool calc_secure, + pe_working_set_t *data_set); + op_digest_cache_t *rsc_action_digest_cmp(pe_resource_t * rsc, xmlNode * xml_op, pe_node_t * node, pe_working_set_t * data_set); diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index a78bd24..c441c71 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -2034,139 +2034,241 @@ append_versioned_params(xmlNode *versioned_params, const char *ra_version, xmlNo } g_hash_table_destroy(hash); } + +static void +append_all_versioned_params(pe_resource_t *rsc, pe_node_t *node, + pe_action_t *action, xmlNode *xml_op, + pe_working_set_t *data_set) +{ + const char *ra_version = NULL; + xmlNode *local_versioned_params = NULL; + pe_rsc_action_details_t *details = pe_rsc_action_details(action); + + local_versioned_params = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS); + pe_get_versioned_attributes(local_versioned_params, rsc, node, data_set); + if (xml_op != NULL) { + ra_version = crm_element_value(xml_op, XML_ATTR_RA_VERSION); + } + append_versioned_params(local_versioned_params, ra_version, + data->params_all); + append_versioned_params(rsc->versioned_parameters, ra_version, + data->params_all); + append_versioned_params(details->versioned_parameters, ra_version, + data->params_all); +} #endif /*! * \internal - * \brief Calculate action digests and store in node's digest cache + * \brief Add digest of all parameters to a digest cache entry * - * \param[in] rsc Resource that action was for - * \param[in] task Name of action performed - * \param[in] key Action's task key - * \param[in] node Node action was performed on - * \param[in] xml_op XML of operation in CIB status (if available) - * \param[in] calc_secure Whether to calculate secure digest - * \param[in] data_set Cluster working set - * - * \return Pointer to node's digest cache entry + * \param[out] data Digest cache entry to modify + * \param[in] rsc Resource that action was for + * \param[in] node Node action was performed on + * \param[in] task Name of action performed + * \param[in] key Action's task key + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] op_version CRM feature set to use for digest calculation + * \param[in] data_set Cluster working set */ -static op_digest_cache_t * -rsc_action_digest(pe_resource_t *rsc, const char *task, const char *key, - pe_node_t *node, xmlNode *xml_op, bool calc_secure, - pe_working_set_t *data_set) +static void +calculate_main_digest(op_digest_cache_t *data, pe_resource_t *rsc, + pe_node_t *node, const char *task, const char *key, + xmlNode *xml_op, const char *op_version, + pe_working_set_t *data_set) { - op_digest_cache_t *data = NULL; + pe_action_t *action = NULL; + GHashTable *local_rsc_params = crm_str_table_new(); - data = g_hash_table_lookup(node->details->digest_cache, key); - if (data == NULL) { - GHashTable *local_rsc_params = crm_str_table_new(); - pe_action_t *action = custom_action(rsc, strdup(key), task, node, TRUE, FALSE, data_set); -#if ENABLE_VERSIONED_ATTRS - xmlNode *local_versioned_params = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS); - const char *ra_version = NULL; -#endif + get_rsc_attributes(local_rsc_params, rsc, node, data_set); - const char *op_version = NULL; - const char *restart_list = NULL; - const char *secure_list = NULL; + data->params_all = create_xml_node(NULL, XML_TAG_PARAMS); - data = calloc(1, sizeof(op_digest_cache_t)); - CRM_ASSERT(data != NULL); + /* REMOTE_CONTAINER_HACK: Allow Pacemaker Remote nodes to run containers + * that themselves are Pacemaker Remote nodes + */ + if (pe__add_bundle_remote_name(rsc, data->params_all, + XML_RSC_ATTR_REMOTE_RA_ADDR)) { + crm_trace("Set address for bundle connection %s (on %s)", + rsc->id, node->details->uname); + } + + action = custom_action(rsc, strdup(key), task, node, TRUE, FALSE, data_set); + g_hash_table_foreach(local_rsc_params, hash2field, data->params_all); + g_hash_table_foreach(action->extra, hash2field, data->params_all); + g_hash_table_foreach(rsc->parameters, hash2field, data->params_all); + g_hash_table_foreach(action->meta, hash2metafield, data->params_all); - get_rsc_attributes(local_rsc_params, rsc, node, data_set); #if ENABLE_VERSIONED_ATTRS - pe_get_versioned_attributes(local_versioned_params, rsc, node, data_set); + append_all_versioned_params(rsc, node, action, xml_op, data_set); #endif - data->params_all = create_xml_node(NULL, XML_TAG_PARAMS); + pcmk__filter_op_for_digest(data->params_all); - // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside - if (pe__add_bundle_remote_name(rsc, data->params_all, - XML_RSC_ATTR_REMOTE_RA_ADDR)) { - crm_trace("Set address for bundle connection %s (on %s)", - rsc->id, node->details->uname); - } + g_hash_table_destroy(local_rsc_params); + pe_free_action(action); - g_hash_table_foreach(local_rsc_params, hash2field, data->params_all); - g_hash_table_foreach(action->extra, hash2field, data->params_all); - g_hash_table_foreach(rsc->parameters, hash2field, data->params_all); - g_hash_table_foreach(action->meta, hash2metafield, data->params_all); + data->digest_all_calc = calculate_operation_digest(data->params_all, + op_version); +} - if(xml_op) { - secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE); - restart_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART); +/*! + * \internal + * \brief Add secure digest to a digest cache entry + * + * \param[out] data Digest cache entry to modify + * \param[in] rsc Resource that action was for + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] op_version CRM feature set to use for digest calculation + */ +static void +calculate_secure_digest(op_digest_cache_t *data, pe_resource_t *rsc, + xmlNode *xml_op, const char *op_version) +{ + const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + const char *secure_list = NULL; - op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); -#if ENABLE_VERSIONED_ATTRS - ra_version = crm_element_value(xml_op, XML_ATTR_RA_VERSION); -#endif + if (xml_op == NULL) { + secure_list = " passwd password user "; + } else { + secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE); + } - } else { - secure_list = " passwd password user "; - op_version = CRM_FEATURE_SET; + /* The controller doesn't create a digest of *all* non-sensitive + * parameters, only those listed in resource agent meta-data. The + * equivalent here is rsc->parameters. + */ + data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS); + g_hash_table_foreach(rsc->parameters, hash2field, data->params_secure); + if (secure_list != NULL) { + filter_parameters(data->params_secure, secure_list, FALSE); + } + if (pcmk_is_set(pcmk_get_ra_caps(class), + pcmk_ra_cap_fence_params)) { + /* For stonith resources, Pacemaker adds special parameters, + * but these are not listed in fence agent meta-data, so the + * controller will not hash them. That means we have to filter + * them out before calculating our hash for comparison. + */ + for (xmlAttrPtr iter = data->params_secure->properties; + iter != NULL; ) { + const char *prop_name = (const char *) iter->name; + + iter = iter->next; // Grab next now in case we remove current + if (pcmk_stonith_param(prop_name)) { + xml_remove_prop(data->params_secure, prop_name); + } } + } + data->digest_secure_calc = calculate_operation_digest(data->params_secure, + op_version); +} -#if ENABLE_VERSIONED_ATTRS - append_versioned_params(local_versioned_params, ra_version, data->params_all); - append_versioned_params(rsc->versioned_parameters, ra_version, data->params_all); +/*! + * \internal + * \brief Add restart digest to a digest cache entry + * + * \param[out] data Digest cache entry to modify + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] op_version CRM feature set to use for digest calculation + */ +static void +calculate_restart_digest(op_digest_cache_t *data, xmlNode *xml_op, + const char *op_version) +{ + const char *value = NULL; - { - pe_rsc_action_details_t *details = pe_rsc_action_details(action); - append_versioned_params(details->versioned_parameters, ra_version, data->params_all); - } -#endif + // We must have XML of resource operation history + if (xml_op == NULL) { + return; + } - pcmk__filter_op_for_digest(data->params_all); + // And the history must have a restart digest to compare against + if (crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) == NULL) { + return; + } - g_hash_table_destroy(local_rsc_params); - pe_free_action(action); + // Start with a copy of all parameters + data->params_restart = copy_xml(data->params_all); - data->digest_all_calc = calculate_operation_digest(data->params_all, op_version); + // Then filter out reloadable parameters, if any + value = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART); + if (value != NULL) { + filter_parameters(data->params_restart, value, TRUE); + } - if (calc_secure) { - const char *class = crm_element_value(rsc->xml, - XML_AGENT_ATTR_CLASS); + value = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + data->digest_restart_calc = calculate_operation_digest(data->params_restart, + value); +} - /* The controller doesn't create a digest of *all* non-sensitive - * parameters, only those listed in resource agent meta-data. The - * equivalent here is rsc->parameters. - */ - data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS); - g_hash_table_foreach(rsc->parameters, hash2field, data->params_secure); - if(secure_list) { - filter_parameters(data->params_secure, secure_list, FALSE); - } - if (pcmk_is_set(pcmk_get_ra_caps(class), - pcmk_ra_cap_fence_params)) { - /* For stonith resources, Pacemaker adds special parameters, - * but these are not listed in fence agent meta-data, so the - * controller will not hash them. That means we have to filter - * them out before calculating our hash for comparison. - */ - for (xmlAttrPtr iter = data->params_secure->properties; - iter != NULL; ) { - const char *prop_name = (const char *) iter->name; +/*! + * \internal + * \brief Create a new digest cache entry with calculated digests + * + * \param[in] rsc Resource that action was for + * \param[in] task Name of action performed + * \param[in] key Action's task key + * \param[in] node Node action was performed on + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] calc_secure Whether to calculate secure digest + * \param[in] data_set Cluster working set + * + * \return Pointer to new digest cache entry (or NULL on memory error) + * \note It is the caller's responsibility to free the result using + * destroy_digest_cache(). + */ +op_digest_cache_t * +pe__calculate_digests(pe_resource_t *rsc, const char *task, const char *key, + pe_node_t *node, xmlNode *xml_op, bool calc_secure, + pe_working_set_t *data_set) +{ + op_digest_cache_t *data = calloc(1, sizeof(op_digest_cache_t)); + const char *op_version = CRM_FEATURE_SET; - iter = iter->next; // Grab next now in case we remove current - if (pcmk_stonith_param(prop_name)) { - xml_remove_prop(data->params_secure, prop_name); - } - } - } - data->digest_secure_calc = calculate_operation_digest(data->params_secure, op_version); - } + if (data == NULL) { + return NULL; + } + if (xml_op != NULL) { + op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + } + calculate_main_digest(data, rsc, node, task, key, xml_op, op_version, + data_set); + if (calc_secure) { + calculate_secure_digest(data, rsc, xml_op, op_version); + } + calculate_restart_digest(data, xml_op, op_version); + return data; +} - if(xml_op && crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) != NULL) { - data->params_restart = copy_xml(data->params_all); - if (restart_list) { - filter_parameters(data->params_restart, restart_list, TRUE); - } - data->digest_restart_calc = calculate_operation_digest(data->params_restart, op_version); - } +/*! + * \internal + * \brief Calculate action digests and store in node's digest cache + * + * \param[in] rsc Resource that action was for + * \param[in] task Name of action performed + * \param[in] key Action's task key + * \param[in] node Node action was performed on + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] calc_secure Whether to calculate secure digest + * \param[in] data_set Cluster working set + * + * \return Pointer to node's digest cache entry + */ +static op_digest_cache_t * +rsc_action_digest(pe_resource_t *rsc, const char *task, const char *key, + pe_node_t *node, xmlNode *xml_op, bool calc_secure, + pe_working_set_t *data_set) +{ + op_digest_cache_t *data = NULL; + data = g_hash_table_lookup(node->details->digest_cache, key); + if (data == NULL) { + data = pe__calculate_digests(rsc, task, key, node, xml_op, calc_secure, + data_set); + CRM_ASSERT(data != NULL); g_hash_table_insert(node->details->digest_cache, strdup(key), data); } - return data; } -- 1.8.3.1 From 5827da6cdd6a49590765c871ce81186af9eead19 Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Tue, 3 Nov 2020 16:44:09 -0600 Subject: [PATCH 2/5] Refactor: scheduler: expose digest free function ... for future reuse. Also rename per current guidelines. --- include/crm/pengine/internal.h | 2 ++ lib/pengine/unpack.c | 31 +++++++++++++++++++++---------- lib/pengine/utils.c | 2 +- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h index 7f22512..7a38234 100644 --- a/include/crm/pengine/internal.h +++ b/include/crm/pengine/internal.h @@ -533,6 +533,8 @@ op_digest_cache_t *pe__calculate_digests(pe_resource_t *rsc, const char *task, xmlNode *xml_op, bool calc_secure, pe_working_set_t *data_set); +void pe__free_digests(gpointer ptr); + op_digest_cache_t *rsc_action_digest_cmp(pe_resource_t * rsc, xmlNode * xml_op, pe_node_t * node, pe_working_set_t * data_set); diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c index 44dba47..4655c7e 100644 --- a/lib/pengine/unpack.c +++ b/lib/pengine/unpack.c @@ -376,20 +376,31 @@ unpack_config(xmlNode * config, pe_working_set_t * data_set) return TRUE; } -static void -destroy_digest_cache(gpointer ptr) +/*! + * \internal + * \brief Free an operation digest cache entry + * + * \param[in] ptr Pointer to cache entry to free + * + * \note The argument is a gpointer so this can be used as a hash table + * free function. + */ +void +pe__free_digests(gpointer ptr) { op_digest_cache_t *data = ptr; - free_xml(data->params_all); - free_xml(data->params_secure); - free_xml(data->params_restart); + if (data != NULL) { + free_xml(data->params_all); + free_xml(data->params_secure); + free_xml(data->params_restart); - free(data->digest_all_calc); - free(data->digest_restart_calc); - free(data->digest_secure_calc); + free(data->digest_all_calc); + free(data->digest_restart_calc); + free(data->digest_secure_calc); - free(data); + free(data); + } } pe_node_t * @@ -446,7 +457,7 @@ pe_create_node(const char *id, const char *uname, const char *type, new_node->details->digest_cache = g_hash_table_new_full(crm_str_hash, g_str_equal, free, - destroy_digest_cache); + pe__free_digests); data_set->nodes = g_list_insert_sorted(data_set->nodes, new_node, sort_node_uname); return new_node; diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index c441c71..bded23c 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -2216,7 +2216,7 @@ calculate_restart_digest(op_digest_cache_t *data, xmlNode *xml_op, * * \return Pointer to new digest cache entry (or NULL on memory error) * \note It is the caller's responsibility to free the result using - * destroy_digest_cache(). + * pe__free_digests(). */ op_digest_cache_t * pe__calculate_digests(pe_resource_t *rsc, const char *task, const char *key, -- 1.8.3.1 From ab184d36cc34776aab992d606c2f4e9ceb49a2ba Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Tue, 3 Nov 2020 16:52:50 -0600 Subject: [PATCH 3/5] Refactor: scheduler: expose fencing digest comparison function within library ... for future separation into different file. Also rename per current guidelines. --- lib/pengine/pe_status_private.h | 6 ++++++ lib/pengine/utils.c | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/pengine/pe_status_private.h b/lib/pengine/pe_status_private.h index 0f3814f..360f280 100644 --- a/lib/pengine/pe_status_private.h +++ b/lib/pengine/pe_status_private.h @@ -65,4 +65,10 @@ gboolean unpack_tags(xmlNode *xml_tags, pe_working_set_t *data_set); G_GNUC_INTERNAL gboolean unpack_status(xmlNode *status, pe_working_set_t *data_set); +G_GNUC_INTERNAL +op_digest_cache_t *pe__compare_fencing_digest(pe_resource_t *rsc, + const char *agent, + pe_node_t *node, + pe_working_set_t *data_set); + #endif // PE_STATUS_PRIVATE__H diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index bded23c..30cec9e 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -20,6 +20,8 @@ #include #include +#include "pe_status_private.h" + extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root); void print_str_str(gpointer key, gpointer value, gpointer user_data); gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data); @@ -2401,9 +2403,9 @@ unfencing_digest_matches(const char *rsc_id, const char *agent, * * \return Node's digest cache entry */ -static op_digest_cache_t * -fencing_action_digest_cmp(pe_resource_t *rsc, const char *agent, - pe_node_t *node, pe_working_set_t *data_set) +op_digest_cache_t * +pe__compare_fencing_digest(pe_resource_t *rsc, const char *agent, + pe_node_t *node, pe_working_set_t *data_set) { const char *node_summary = NULL; @@ -2613,7 +2615,7 @@ pe_fence_op(pe_node_t * node, const char *op, bool optional, const char *reason, XML_ATTR_TYPE); op_digest_cache_t *data = NULL; - data = fencing_action_digest_cmp(match, agent, node, data_set); + data = pe__compare_fencing_digest(match, agent, node, data_set); if(data->rc == RSC_DIGEST_ALL) { optional = FALSE; crm_notice("Unfencing %s (remote): because the definition of %s changed", node->details->uname, match->id); -- 1.8.3.1 From 55a5299d815992df9122d4b1e67e3c2f5a969c43 Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Fri, 23 Oct 2020 11:30:16 -0500 Subject: [PATCH 4/5] Refactor: libpe_status: separate digest-related code into own file There are no changes in the code. Before: 4035 lib/pengine/unpack.c 2966 lib/pengine/utils.c 7001 total After: 509 lib/pengine/pe_digest.c 4008 lib/pengine/unpack.c 2502 lib/pengine/utils.c 7019 total --- lib/pengine/Makefile.am | 1 + lib/pengine/pe_digest.c | 509 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/pengine/unpack.c | 27 --- lib/pengine/utils.c | 464 ------------------------------------------- 4 files changed, 510 insertions(+), 491 deletions(-) create mode 100644 lib/pengine/pe_digest.c diff --git a/lib/pengine/Makefile.am b/lib/pengine/Makefile.am index 6289bfc..258f594 100644 --- a/lib/pengine/Makefile.am +++ b/lib/pengine/Makefile.am @@ -39,6 +39,7 @@ libpe_status_la_SOURCES += complex.c libpe_status_la_SOURCES += failcounts.c libpe_status_la_SOURCES += group.c libpe_status_la_SOURCES += native.c +libpe_status_la_SOURCES += pe_digest.c libpe_status_la_SOURCES += remote.c libpe_status_la_SOURCES += rules.c libpe_status_la_SOURCES += status.c diff --git a/lib/pengine/pe_digest.c b/lib/pengine/pe_digest.c new file mode 100644 index 0000000..b54210c --- /dev/null +++ b/lib/pengine/pe_digest.c @@ -0,0 +1,509 @@ +/* + * Copyright 2004-2020 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include "pe_status_private.h" + +/*! + * \internal + * \brief Free an operation digest cache entry + * + * \param[in] ptr Pointer to cache entry to free + * + * \note The argument is a gpointer so this can be used as a hash table + * free function. + */ +void +pe__free_digests(gpointer ptr) +{ + op_digest_cache_t *data = ptr; + + if (data != NULL) { + free_xml(data->params_all); + free_xml(data->params_secure); + free_xml(data->params_restart); + + free(data->digest_all_calc); + free(data->digest_restart_calc); + free(data->digest_secure_calc); + + free(data); + } +} + +static void +filter_parameters(xmlNode * param_set, const char *param_string, bool need_present) +{ + if (param_set && param_string) { + xmlAttrPtr xIter = param_set->properties; + + while (xIter) { + const char *prop_name = (const char *)xIter->name; + char *name = crm_strdup_printf(" %s ", prop_name); + char *match = strstr(param_string, name); + + free(name); + + // Do now, because current entry might get removed below + xIter = xIter->next; + + if (need_present && match == NULL) { + crm_trace("%s not found in %s", prop_name, param_string); + xml_remove_prop(param_set, prop_name); + + } else if (need_present == FALSE && match) { + crm_trace("%s found in %s", prop_name, param_string); + xml_remove_prop(param_set, prop_name); + } + } + } +} + +#if ENABLE_VERSIONED_ATTRS +static void +append_versioned_params(xmlNode *versioned_params, const char *ra_version, xmlNode *params) +{ + GHashTable *hash = pe_unpack_versioned_parameters(versioned_params, ra_version); + char *key = NULL; + char *value = NULL; + GHashTableIter iter; + + g_hash_table_iter_init(&iter, hash); + while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) { + crm_xml_add(params, key, value); + } + g_hash_table_destroy(hash); +} + +static void +append_all_versioned_params(pe_resource_t *rsc, pe_node_t *node, + pe_action_t *action, xmlNode *xml_op, + pe_working_set_t *data_set) +{ + const char *ra_version = NULL; + xmlNode *local_versioned_params = NULL; + pe_rsc_action_details_t *details = pe_rsc_action_details(action); + + local_versioned_params = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS); + pe_get_versioned_attributes(local_versioned_params, rsc, node, data_set); + if (xml_op != NULL) { + ra_version = crm_element_value(xml_op, XML_ATTR_RA_VERSION); + } + append_versioned_params(local_versioned_params, ra_version, + data->params_all); + append_versioned_params(rsc->versioned_parameters, ra_version, + data->params_all); + append_versioned_params(details->versioned_parameters, ra_version, + data->params_all); +} +#endif + +/*! + * \internal + * \brief Add digest of all parameters to a digest cache entry + * + * \param[out] data Digest cache entry to modify + * \param[in] rsc Resource that action was for + * \param[in] node Node action was performed on + * \param[in] task Name of action performed + * \param[in] key Action's task key + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] op_version CRM feature set to use for digest calculation + * \param[in] data_set Cluster working set + */ +static void +calculate_main_digest(op_digest_cache_t *data, pe_resource_t *rsc, + pe_node_t *node, const char *task, const char *key, + xmlNode *xml_op, const char *op_version, + pe_working_set_t *data_set) +{ + pe_action_t *action = NULL; + GHashTable *local_rsc_params = crm_str_table_new(); + + get_rsc_attributes(local_rsc_params, rsc, node, data_set); + + data->params_all = create_xml_node(NULL, XML_TAG_PARAMS); + + /* REMOTE_CONTAINER_HACK: Allow Pacemaker Remote nodes to run containers + * that themselves are Pacemaker Remote nodes + */ + if (pe__add_bundle_remote_name(rsc, data->params_all, + XML_RSC_ATTR_REMOTE_RA_ADDR)) { + crm_trace("Set address for bundle connection %s (on %s)", + rsc->id, node->details->uname); + } + + action = custom_action(rsc, strdup(key), task, node, TRUE, FALSE, data_set); + g_hash_table_foreach(local_rsc_params, hash2field, data->params_all); + g_hash_table_foreach(action->extra, hash2field, data->params_all); + g_hash_table_foreach(rsc->parameters, hash2field, data->params_all); + g_hash_table_foreach(action->meta, hash2metafield, data->params_all); + +#if ENABLE_VERSIONED_ATTRS + append_all_versioned_params(rsc, node, action, xml_op, data_set); +#endif + + pcmk__filter_op_for_digest(data->params_all); + + g_hash_table_destroy(local_rsc_params); + pe_free_action(action); + + data->digest_all_calc = calculate_operation_digest(data->params_all, + op_version); +} + +/*! + * \internal + * \brief Add secure digest to a digest cache entry + * + * \param[out] data Digest cache entry to modify + * \param[in] rsc Resource that action was for + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] op_version CRM feature set to use for digest calculation + */ +static void +calculate_secure_digest(op_digest_cache_t *data, pe_resource_t *rsc, + xmlNode *xml_op, const char *op_version) +{ + const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); + const char *secure_list = NULL; + + if (xml_op == NULL) { + secure_list = " passwd password user "; + } else { + secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE); + } + + /* The controller doesn't create a digest of *all* non-sensitive + * parameters, only those listed in resource agent meta-data. The + * equivalent here is rsc->parameters. + */ + data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS); + g_hash_table_foreach(rsc->parameters, hash2field, data->params_secure); + if (secure_list != NULL) { + filter_parameters(data->params_secure, secure_list, FALSE); + } + if (pcmk_is_set(pcmk_get_ra_caps(class), + pcmk_ra_cap_fence_params)) { + /* For stonith resources, Pacemaker adds special parameters, + * but these are not listed in fence agent meta-data, so the + * controller will not hash them. That means we have to filter + * them out before calculating our hash for comparison. + */ + for (xmlAttrPtr iter = data->params_secure->properties; + iter != NULL; ) { + const char *prop_name = (const char *) iter->name; + + iter = iter->next; // Grab next now in case we remove current + if (pcmk_stonith_param(prop_name)) { + xml_remove_prop(data->params_secure, prop_name); + } + } + } + data->digest_secure_calc = calculate_operation_digest(data->params_secure, + op_version); +} + +/*! + * \internal + * \brief Add restart digest to a digest cache entry + * + * \param[out] data Digest cache entry to modify + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] op_version CRM feature set to use for digest calculation + */ +static void +calculate_restart_digest(op_digest_cache_t *data, xmlNode *xml_op, + const char *op_version) +{ + const char *value = NULL; + + // We must have XML of resource operation history + if (xml_op == NULL) { + return; + } + + // And the history must have a restart digest to compare against + if (crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) == NULL) { + return; + } + + // Start with a copy of all parameters + data->params_restart = copy_xml(data->params_all); + + // Then filter out reloadable parameters, if any + value = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART); + if (value != NULL) { + filter_parameters(data->params_restart, value, TRUE); + } + + value = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + data->digest_restart_calc = calculate_operation_digest(data->params_restart, + value); +} + +/*! + * \internal + * \brief Create a new digest cache entry with calculated digests + * + * \param[in] rsc Resource that action was for + * \param[in] task Name of action performed + * \param[in] key Action's task key + * \param[in] node Node action was performed on + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] calc_secure Whether to calculate secure digest + * \param[in] data_set Cluster working set + * + * \return Pointer to new digest cache entry (or NULL on memory error) + * \note It is the caller's responsibility to free the result using + * pe__free_digests(). + */ +op_digest_cache_t * +pe__calculate_digests(pe_resource_t *rsc, const char *task, const char *key, + pe_node_t *node, xmlNode *xml_op, bool calc_secure, + pe_working_set_t *data_set) +{ + op_digest_cache_t *data = calloc(1, sizeof(op_digest_cache_t)); + const char *op_version = CRM_FEATURE_SET; + + if (data == NULL) { + return NULL; + } + if (xml_op != NULL) { + op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + } + calculate_main_digest(data, rsc, node, task, key, xml_op, op_version, + data_set); + if (calc_secure) { + calculate_secure_digest(data, rsc, xml_op, op_version); + } + calculate_restart_digest(data, xml_op, op_version); + return data; +} + +/*! + * \internal + * \brief Calculate action digests and store in node's digest cache + * + * \param[in] rsc Resource that action was for + * \param[in] task Name of action performed + * \param[in] key Action's task key + * \param[in] node Node action was performed on + * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] calc_secure Whether to calculate secure digest + * \param[in] data_set Cluster working set + * + * \return Pointer to node's digest cache entry + */ +static op_digest_cache_t * +rsc_action_digest(pe_resource_t *rsc, const char *task, const char *key, + pe_node_t *node, xmlNode *xml_op, bool calc_secure, + pe_working_set_t *data_set) +{ + op_digest_cache_t *data = NULL; + + data = g_hash_table_lookup(node->details->digest_cache, key); + if (data == NULL) { + data = pe__calculate_digests(rsc, task, key, node, xml_op, calc_secure, + data_set); + CRM_ASSERT(data != NULL); + g_hash_table_insert(node->details->digest_cache, strdup(key), data); + } + return data; +} + +op_digest_cache_t * +rsc_action_digest_cmp(pe_resource_t * rsc, xmlNode * xml_op, pe_node_t * node, + pe_working_set_t * data_set) +{ + op_digest_cache_t *data = NULL; + + char *key = NULL; + guint interval_ms = 0; + + const char *op_version; + const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); + const char *digest_all; + const char *digest_restart; + + CRM_ASSERT(node != NULL); + + op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); + digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); + digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST); + + crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); + key = pcmk__op_key(rsc->id, task, interval_ms); + data = rsc_action_digest(rsc, task, key, node, xml_op, + pcmk_is_set(data_set->flags, pe_flag_sanitized), + data_set); + + data->rc = RSC_DIGEST_MATCH; + if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) { + pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (restart:%s) %s", + key, node->details->uname, + crm_str(digest_restart), data->digest_restart_calc, + op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); + data->rc = RSC_DIGEST_RESTART; + + } else if (digest_all == NULL) { + /* it is unknown what the previous op digest was */ + data->rc = RSC_DIGEST_UNKNOWN; + + } else if (strcmp(digest_all, data->digest_all_calc) != 0) { + pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (%s:%s) %s", + key, node->details->uname, + crm_str(digest_all), data->digest_all_calc, + (interval_ms > 0)? "reschedule" : "reload", + op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); + data->rc = RSC_DIGEST_ALL; + } + + free(key); + return data; +} + +/*! + * \internal + * \brief Create an unfencing summary for use in special node attribute + * + * Create a string combining a fence device's resource ID, agent type, and + * parameter digest (whether for all parameters or just non-private parameters). + * This can be stored in a special node attribute, allowing us to detect changes + * in either the agent type or parameters, to know whether unfencing must be + * redone or can be safely skipped when the device's history is cleaned. + * + * \param[in] rsc_id Fence device resource ID + * \param[in] agent_type Fence device agent + * \param[in] param_digest Fence device parameter digest + * + * \return Newly allocated string with unfencing digest + * \note The caller is responsible for freeing the result. + */ +static inline char * +create_unfencing_summary(const char *rsc_id, const char *agent_type, + const char *param_digest) +{ + return crm_strdup_printf("%s:%s:%s", rsc_id, agent_type, param_digest); +} + +/*! + * \internal + * \brief Check whether a node can skip unfencing + * + * Check whether a fence device's current definition matches a node's + * stored summary of when it was last unfenced by the device. + * + * \param[in] rsc_id Fence device's resource ID + * \param[in] agent Fence device's agent type + * \param[in] digest_calc Fence device's current parameter digest + * \param[in] node_summary Value of node's special unfencing node attribute + * (a comma-separated list of unfencing summaries for + * all devices that have unfenced this node) + * + * \return TRUE if digest matches, FALSE otherwise + */ +static bool +unfencing_digest_matches(const char *rsc_id, const char *agent, + const char *digest_calc, const char *node_summary) +{ + bool matches = FALSE; + + if (rsc_id && agent && digest_calc && node_summary) { + char *search_secure = create_unfencing_summary(rsc_id, agent, + digest_calc); + + /* The digest was calculated including the device ID and agent, + * so there is no risk of collision using strstr(). + */ + matches = (strstr(node_summary, search_secure) != NULL); + crm_trace("Calculated unfencing digest '%s' %sfound in '%s'", + search_secure, matches? "" : "not ", node_summary); + free(search_secure); + } + return matches; +} + +/* Magic string to use as action name for digest cache entries used for + * unfencing checks. This is not a real action name (i.e. "on"), so + * check_action_definition() won't confuse these entries with real actions. + */ +#define STONITH_DIGEST_TASK "stonith-on" + +/*! + * \internal + * \brief Calculate fence device digests and digest comparison result + * + * \param[in] rsc Fence device resource + * \param[in] agent Fence device's agent type + * \param[in] node Node with digest cache to use + * \param[in] data_set Cluster working set + * + * \return Node's digest cache entry + */ +op_digest_cache_t * +pe__compare_fencing_digest(pe_resource_t *rsc, const char *agent, + pe_node_t *node, pe_working_set_t *data_set) +{ + const char *node_summary = NULL; + + // Calculate device's current parameter digests + char *key = pcmk__op_key(rsc->id, STONITH_DIGEST_TASK, 0); + op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, key, + node, NULL, TRUE, data_set); + + free(key); + + // Check whether node has special unfencing summary node attribute + node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL); + if (node_summary == NULL) { + data->rc = RSC_DIGEST_UNKNOWN; + return data; + } + + // Check whether full parameter digest matches + if (unfencing_digest_matches(rsc->id, agent, data->digest_all_calc, + node_summary)) { + data->rc = RSC_DIGEST_MATCH; + return data; + } + + // Check whether secure parameter digest matches + node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE); + if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc, + node_summary)) { + data->rc = RSC_DIGEST_MATCH; + if (pcmk_is_set(data_set->flags, pe_flag_stdout)) { + printf("Only 'private' parameters to %s for unfencing %s changed\n", + rsc->id, node->details->uname); + } + return data; + } + + // Parameters don't match + data->rc = RSC_DIGEST_ALL; + if (pcmk_is_set(data_set->flags, (pe_flag_sanitized|pe_flag_stdout)) + && data->digest_secure_calc) { + char *digest = create_unfencing_summary(rsc->id, agent, + data->digest_secure_calc); + + printf("Parameters to %s for unfencing %s changed, try '%s'\n", + rsc->id, node->details->uname, digest); + free(digest); + } + return data; +} diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c index 4655c7e..a15bb92 100644 --- a/lib/pengine/unpack.c +++ b/lib/pengine/unpack.c @@ -376,33 +376,6 @@ unpack_config(xmlNode * config, pe_working_set_t * data_set) return TRUE; } -/*! - * \internal - * \brief Free an operation digest cache entry - * - * \param[in] ptr Pointer to cache entry to free - * - * \note The argument is a gpointer so this can be used as a hash table - * free function. - */ -void -pe__free_digests(gpointer ptr) -{ - op_digest_cache_t *data = ptr; - - if (data != NULL) { - free_xml(data->params_all); - free_xml(data->params_secure); - free_xml(data->params_restart); - - free(data->digest_all_calc); - free(data->digest_restart_calc); - free(data->digest_secure_calc); - - free(data); - } -} - pe_node_t * pe_create_node(const char *id, const char *uname, const char *type, const char *score, pe_working_set_t * data_set) diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index 30cec9e..04110be 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -19,7 +19,6 @@ #include #include - #include "pe_status_private.h" extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root); @@ -1993,469 +1992,6 @@ ticket_new(const char *ticket_id, pe_working_set_t * data_set) return ticket; } -static void -filter_parameters(xmlNode * param_set, const char *param_string, bool need_present) -{ - if (param_set && param_string) { - xmlAttrPtr xIter = param_set->properties; - - while (xIter) { - const char *prop_name = (const char *)xIter->name; - char *name = crm_strdup_printf(" %s ", prop_name); - char *match = strstr(param_string, name); - - free(name); - - // Do now, because current entry might get removed below - xIter = xIter->next; - - if (need_present && match == NULL) { - crm_trace("%s not found in %s", prop_name, param_string); - xml_remove_prop(param_set, prop_name); - - } else if (need_present == FALSE && match) { - crm_trace("%s found in %s", prop_name, param_string); - xml_remove_prop(param_set, prop_name); - } - } - } -} - -#if ENABLE_VERSIONED_ATTRS -static void -append_versioned_params(xmlNode *versioned_params, const char *ra_version, xmlNode *params) -{ - GHashTable *hash = pe_unpack_versioned_parameters(versioned_params, ra_version); - char *key = NULL; - char *value = NULL; - GHashTableIter iter; - - g_hash_table_iter_init(&iter, hash); - while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) { - crm_xml_add(params, key, value); - } - g_hash_table_destroy(hash); -} - -static void -append_all_versioned_params(pe_resource_t *rsc, pe_node_t *node, - pe_action_t *action, xmlNode *xml_op, - pe_working_set_t *data_set) -{ - const char *ra_version = NULL; - xmlNode *local_versioned_params = NULL; - pe_rsc_action_details_t *details = pe_rsc_action_details(action); - - local_versioned_params = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS); - pe_get_versioned_attributes(local_versioned_params, rsc, node, data_set); - if (xml_op != NULL) { - ra_version = crm_element_value(xml_op, XML_ATTR_RA_VERSION); - } - append_versioned_params(local_versioned_params, ra_version, - data->params_all); - append_versioned_params(rsc->versioned_parameters, ra_version, - data->params_all); - append_versioned_params(details->versioned_parameters, ra_version, - data->params_all); -} -#endif - -/*! - * \internal - * \brief Add digest of all parameters to a digest cache entry - * - * \param[out] data Digest cache entry to modify - * \param[in] rsc Resource that action was for - * \param[in] node Node action was performed on - * \param[in] task Name of action performed - * \param[in] key Action's task key - * \param[in] xml_op XML of operation in CIB status (if available) - * \param[in] op_version CRM feature set to use for digest calculation - * \param[in] data_set Cluster working set - */ -static void -calculate_main_digest(op_digest_cache_t *data, pe_resource_t *rsc, - pe_node_t *node, const char *task, const char *key, - xmlNode *xml_op, const char *op_version, - pe_working_set_t *data_set) -{ - pe_action_t *action = NULL; - GHashTable *local_rsc_params = crm_str_table_new(); - - get_rsc_attributes(local_rsc_params, rsc, node, data_set); - - data->params_all = create_xml_node(NULL, XML_TAG_PARAMS); - - /* REMOTE_CONTAINER_HACK: Allow Pacemaker Remote nodes to run containers - * that themselves are Pacemaker Remote nodes - */ - if (pe__add_bundle_remote_name(rsc, data->params_all, - XML_RSC_ATTR_REMOTE_RA_ADDR)) { - crm_trace("Set address for bundle connection %s (on %s)", - rsc->id, node->details->uname); - } - - action = custom_action(rsc, strdup(key), task, node, TRUE, FALSE, data_set); - g_hash_table_foreach(local_rsc_params, hash2field, data->params_all); - g_hash_table_foreach(action->extra, hash2field, data->params_all); - g_hash_table_foreach(rsc->parameters, hash2field, data->params_all); - g_hash_table_foreach(action->meta, hash2metafield, data->params_all); - -#if ENABLE_VERSIONED_ATTRS - append_all_versioned_params(rsc, node, action, xml_op, data_set); -#endif - - pcmk__filter_op_for_digest(data->params_all); - - g_hash_table_destroy(local_rsc_params); - pe_free_action(action); - - data->digest_all_calc = calculate_operation_digest(data->params_all, - op_version); -} - -/*! - * \internal - * \brief Add secure digest to a digest cache entry - * - * \param[out] data Digest cache entry to modify - * \param[in] rsc Resource that action was for - * \param[in] xml_op XML of operation in CIB status (if available) - * \param[in] op_version CRM feature set to use for digest calculation - */ -static void -calculate_secure_digest(op_digest_cache_t *data, pe_resource_t *rsc, - xmlNode *xml_op, const char *op_version) -{ - const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); - const char *secure_list = NULL; - - if (xml_op == NULL) { - secure_list = " passwd password user "; - } else { - secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE); - } - - /* The controller doesn't create a digest of *all* non-sensitive - * parameters, only those listed in resource agent meta-data. The - * equivalent here is rsc->parameters. - */ - data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS); - g_hash_table_foreach(rsc->parameters, hash2field, data->params_secure); - if (secure_list != NULL) { - filter_parameters(data->params_secure, secure_list, FALSE); - } - if (pcmk_is_set(pcmk_get_ra_caps(class), - pcmk_ra_cap_fence_params)) { - /* For stonith resources, Pacemaker adds special parameters, - * but these are not listed in fence agent meta-data, so the - * controller will not hash them. That means we have to filter - * them out before calculating our hash for comparison. - */ - for (xmlAttrPtr iter = data->params_secure->properties; - iter != NULL; ) { - const char *prop_name = (const char *) iter->name; - - iter = iter->next; // Grab next now in case we remove current - if (pcmk_stonith_param(prop_name)) { - xml_remove_prop(data->params_secure, prop_name); - } - } - } - data->digest_secure_calc = calculate_operation_digest(data->params_secure, - op_version); -} - -/*! - * \internal - * \brief Add restart digest to a digest cache entry - * - * \param[out] data Digest cache entry to modify - * \param[in] xml_op XML of operation in CIB status (if available) - * \param[in] op_version CRM feature set to use for digest calculation - */ -static void -calculate_restart_digest(op_digest_cache_t *data, xmlNode *xml_op, - const char *op_version) -{ - const char *value = NULL; - - // We must have XML of resource operation history - if (xml_op == NULL) { - return; - } - - // And the history must have a restart digest to compare against - if (crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) == NULL) { - return; - } - - // Start with a copy of all parameters - data->params_restart = copy_xml(data->params_all); - - // Then filter out reloadable parameters, if any - value = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART); - if (value != NULL) { - filter_parameters(data->params_restart, value, TRUE); - } - - value = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); - data->digest_restart_calc = calculate_operation_digest(data->params_restart, - value); -} - -/*! - * \internal - * \brief Create a new digest cache entry with calculated digests - * - * \param[in] rsc Resource that action was for - * \param[in] task Name of action performed - * \param[in] key Action's task key - * \param[in] node Node action was performed on - * \param[in] xml_op XML of operation in CIB status (if available) - * \param[in] calc_secure Whether to calculate secure digest - * \param[in] data_set Cluster working set - * - * \return Pointer to new digest cache entry (or NULL on memory error) - * \note It is the caller's responsibility to free the result using - * pe__free_digests(). - */ -op_digest_cache_t * -pe__calculate_digests(pe_resource_t *rsc, const char *task, const char *key, - pe_node_t *node, xmlNode *xml_op, bool calc_secure, - pe_working_set_t *data_set) -{ - op_digest_cache_t *data = calloc(1, sizeof(op_digest_cache_t)); - const char *op_version = CRM_FEATURE_SET; - - if (data == NULL) { - return NULL; - } - if (xml_op != NULL) { - op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); - } - calculate_main_digest(data, rsc, node, task, key, xml_op, op_version, - data_set); - if (calc_secure) { - calculate_secure_digest(data, rsc, xml_op, op_version); - } - calculate_restart_digest(data, xml_op, op_version); - return data; -} - -/*! - * \internal - * \brief Calculate action digests and store in node's digest cache - * - * \param[in] rsc Resource that action was for - * \param[in] task Name of action performed - * \param[in] key Action's task key - * \param[in] node Node action was performed on - * \param[in] xml_op XML of operation in CIB status (if available) - * \param[in] calc_secure Whether to calculate secure digest - * \param[in] data_set Cluster working set - * - * \return Pointer to node's digest cache entry - */ -static op_digest_cache_t * -rsc_action_digest(pe_resource_t *rsc, const char *task, const char *key, - pe_node_t *node, xmlNode *xml_op, bool calc_secure, - pe_working_set_t *data_set) -{ - op_digest_cache_t *data = NULL; - - data = g_hash_table_lookup(node->details->digest_cache, key); - if (data == NULL) { - data = pe__calculate_digests(rsc, task, key, node, xml_op, calc_secure, - data_set); - CRM_ASSERT(data != NULL); - g_hash_table_insert(node->details->digest_cache, strdup(key), data); - } - return data; -} - -op_digest_cache_t * -rsc_action_digest_cmp(pe_resource_t * rsc, xmlNode * xml_op, pe_node_t * node, - pe_working_set_t * data_set) -{ - op_digest_cache_t *data = NULL; - - char *key = NULL; - guint interval_ms = 0; - - const char *op_version; - const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); - const char *digest_all; - const char *digest_restart; - - CRM_ASSERT(node != NULL); - - op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); - digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); - digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST); - - crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); - key = pcmk__op_key(rsc->id, task, interval_ms); - data = rsc_action_digest(rsc, task, key, node, xml_op, - pcmk_is_set(data_set->flags, pe_flag_sanitized), - data_set); - - data->rc = RSC_DIGEST_MATCH; - if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) { - pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (restart:%s) %s", - key, node->details->uname, - crm_str(digest_restart), data->digest_restart_calc, - op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); - data->rc = RSC_DIGEST_RESTART; - - } else if (digest_all == NULL) { - /* it is unknown what the previous op digest was */ - data->rc = RSC_DIGEST_UNKNOWN; - - } else if (strcmp(digest_all, data->digest_all_calc) != 0) { - pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (%s:%s) %s", - key, node->details->uname, - crm_str(digest_all), data->digest_all_calc, - (interval_ms > 0)? "reschedule" : "reload", - op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); - data->rc = RSC_DIGEST_ALL; - } - - free(key); - return data; -} - -/*! - * \internal - * \brief Create an unfencing summary for use in special node attribute - * - * Create a string combining a fence device's resource ID, agent type, and - * parameter digest (whether for all parameters or just non-private parameters). - * This can be stored in a special node attribute, allowing us to detect changes - * in either the agent type or parameters, to know whether unfencing must be - * redone or can be safely skipped when the device's history is cleaned. - * - * \param[in] rsc_id Fence device resource ID - * \param[in] agent_type Fence device agent - * \param[in] param_digest Fence device parameter digest - * - * \return Newly allocated string with unfencing digest - * \note The caller is responsible for freeing the result. - */ -static inline char * -create_unfencing_summary(const char *rsc_id, const char *agent_type, - const char *param_digest) -{ - return crm_strdup_printf("%s:%s:%s", rsc_id, agent_type, param_digest); -} - -/*! - * \internal - * \brief Check whether a node can skip unfencing - * - * Check whether a fence device's current definition matches a node's - * stored summary of when it was last unfenced by the device. - * - * \param[in] rsc_id Fence device's resource ID - * \param[in] agent Fence device's agent type - * \param[in] digest_calc Fence device's current parameter digest - * \param[in] node_summary Value of node's special unfencing node attribute - * (a comma-separated list of unfencing summaries for - * all devices that have unfenced this node) - * - * \return TRUE if digest matches, FALSE otherwise - */ -static bool -unfencing_digest_matches(const char *rsc_id, const char *agent, - const char *digest_calc, const char *node_summary) -{ - bool matches = FALSE; - - if (rsc_id && agent && digest_calc && node_summary) { - char *search_secure = create_unfencing_summary(rsc_id, agent, - digest_calc); - - /* The digest was calculated including the device ID and agent, - * so there is no risk of collision using strstr(). - */ - matches = (strstr(node_summary, search_secure) != NULL); - crm_trace("Calculated unfencing digest '%s' %sfound in '%s'", - search_secure, matches? "" : "not ", node_summary); - free(search_secure); - } - return matches; -} - -/* Magic string to use as action name for digest cache entries used for - * unfencing checks. This is not a real action name (i.e. "on"), so - * check_action_definition() won't confuse these entries with real actions. - */ -#define STONITH_DIGEST_TASK "stonith-on" - -/*! - * \internal - * \brief Calculate fence device digests and digest comparison result - * - * \param[in] rsc Fence device resource - * \param[in] agent Fence device's agent type - * \param[in] node Node with digest cache to use - * \param[in] data_set Cluster working set - * - * \return Node's digest cache entry - */ -op_digest_cache_t * -pe__compare_fencing_digest(pe_resource_t *rsc, const char *agent, - pe_node_t *node, pe_working_set_t *data_set) -{ - const char *node_summary = NULL; - - // Calculate device's current parameter digests - char *key = pcmk__op_key(rsc->id, STONITH_DIGEST_TASK, 0); - op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, key, - node, NULL, TRUE, data_set); - - free(key); - - // Check whether node has special unfencing summary node attribute - node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL); - if (node_summary == NULL) { - data->rc = RSC_DIGEST_UNKNOWN; - return data; - } - - // Check whether full parameter digest matches - if (unfencing_digest_matches(rsc->id, agent, data->digest_all_calc, - node_summary)) { - data->rc = RSC_DIGEST_MATCH; - return data; - } - - // Check whether secure parameter digest matches - node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE); - if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc, - node_summary)) { - data->rc = RSC_DIGEST_MATCH; - if (pcmk_is_set(data_set->flags, pe_flag_stdout)) { - printf("Only 'private' parameters to %s for unfencing %s changed\n", - rsc->id, node->details->uname); - } - return data; - } - - // Parameters don't match - data->rc = RSC_DIGEST_ALL; - if (pcmk_is_set(data_set->flags, (pe_flag_sanitized|pe_flag_stdout)) - && data->digest_secure_calc) { - char *digest = create_unfencing_summary(rsc->id, agent, - data->digest_secure_calc); - - printf("Parameters to %s for unfencing %s changed, try '%s'\n", - rsc->id, node->details->uname, digest); - free(digest); - } - return data; -} - const char *rsc_printable_id(pe_resource_t *rsc) { if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) { -- 1.8.3.1 From 310ac114613fc6c16f26d95805d934c082d67039 Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Fri, 23 Oct 2020 12:51:10 -0500 Subject: [PATCH 5/5] Refactor: libpe_status: add ability to override configuration for digests This will be needed for a new command-line feature --- include/crm/pengine/internal.h | 3 ++- lib/pengine/pe_digest.c | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h index 7a38234..c4b28cc 100644 --- a/include/crm/pengine/internal.h +++ b/include/crm/pengine/internal.h @@ -530,7 +530,8 @@ typedef struct op_digest_cache_s { op_digest_cache_t *pe__calculate_digests(pe_resource_t *rsc, const char *task, const char *key, pe_node_t *node, - xmlNode *xml_op, bool calc_secure, + xmlNode *xml_op, GHashTable *overrides, + bool calc_secure, pe_working_set_t *data_set); void pe__free_digests(gpointer ptr); diff --git a/lib/pengine/pe_digest.c b/lib/pengine/pe_digest.c index b54210c..5bcd22b 100644 --- a/lib/pengine/pe_digest.c +++ b/lib/pengine/pe_digest.c @@ -123,13 +123,14 @@ append_all_versioned_params(pe_resource_t *rsc, pe_node_t *node, * \param[in] key Action's task key * \param[in] xml_op XML of operation in CIB status (if available) * \param[in] op_version CRM feature set to use for digest calculation + * \param[in] overrides Key/value hash table to override resource parameters * \param[in] data_set Cluster working set */ static void calculate_main_digest(op_digest_cache_t *data, pe_resource_t *rsc, pe_node_t *node, const char *task, const char *key, xmlNode *xml_op, const char *op_version, - pe_working_set_t *data_set) + GHashTable *overrides, pe_working_set_t *data_set) { pe_action_t *action = NULL; GHashTable *local_rsc_params = crm_str_table_new(); @@ -148,6 +149,9 @@ calculate_main_digest(op_digest_cache_t *data, pe_resource_t *rsc, } action = custom_action(rsc, strdup(key), task, node, TRUE, FALSE, data_set); + if (overrides != NULL) { + g_hash_table_foreach(overrides, hash2field, data->params_all); + } g_hash_table_foreach(local_rsc_params, hash2field, data->params_all); g_hash_table_foreach(action->extra, hash2field, data->params_all); g_hash_table_foreach(rsc->parameters, hash2field, data->params_all); @@ -174,10 +178,12 @@ calculate_main_digest(op_digest_cache_t *data, pe_resource_t *rsc, * \param[in] rsc Resource that action was for * \param[in] xml_op XML of operation in CIB status (if available) * \param[in] op_version CRM feature set to use for digest calculation + * \param[in] overrides Key/value hash table to override resource parameters */ static void calculate_secure_digest(op_digest_cache_t *data, pe_resource_t *rsc, - xmlNode *xml_op, const char *op_version) + xmlNode *xml_op, const char *op_version, + GHashTable *overrides) { const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); const char *secure_list = NULL; @@ -193,6 +199,9 @@ calculate_secure_digest(op_digest_cache_t *data, pe_resource_t *rsc, * equivalent here is rsc->parameters. */ data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS); + if (overrides != NULL) { + g_hash_table_foreach(overrides, hash2field, data->params_secure); + } g_hash_table_foreach(rsc->parameters, hash2field, data->params_secure); if (secure_list != NULL) { filter_parameters(data->params_secure, secure_list, FALSE); @@ -225,6 +234,9 @@ calculate_secure_digest(op_digest_cache_t *data, pe_resource_t *rsc, * \param[out] data Digest cache entry to modify * \param[in] xml_op XML of operation in CIB status (if available) * \param[in] op_version CRM feature set to use for digest calculation + * + * \note This function doesn't need to handle overrides because it starts with + * data->params_all, which already has overrides applied. */ static void calculate_restart_digest(op_digest_cache_t *data, xmlNode *xml_op, @@ -265,6 +277,7 @@ calculate_restart_digest(op_digest_cache_t *data, xmlNode *xml_op, * \param[in] key Action's task key * \param[in] node Node action was performed on * \param[in] xml_op XML of operation in CIB status (if available) + * \param[in] overrides Key/value hash table to override resource parameters * \param[in] calc_secure Whether to calculate secure digest * \param[in] data_set Cluster working set * @@ -274,8 +287,8 @@ calculate_restart_digest(op_digest_cache_t *data, xmlNode *xml_op, */ op_digest_cache_t * pe__calculate_digests(pe_resource_t *rsc, const char *task, const char *key, - pe_node_t *node, xmlNode *xml_op, bool calc_secure, - pe_working_set_t *data_set) + pe_node_t *node, xmlNode *xml_op, GHashTable *overrides, + bool calc_secure, pe_working_set_t *data_set) { op_digest_cache_t *data = calloc(1, sizeof(op_digest_cache_t)); const char *op_version = CRM_FEATURE_SET; @@ -287,9 +300,9 @@ pe__calculate_digests(pe_resource_t *rsc, const char *task, const char *key, op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); } calculate_main_digest(data, rsc, node, task, key, xml_op, op_version, - data_set); + overrides, data_set); if (calc_secure) { - calculate_secure_digest(data, rsc, xml_op, op_version); + calculate_secure_digest(data, rsc, xml_op, op_version, overrides); } calculate_restart_digest(data, xml_op, op_version); return data; @@ -318,8 +331,8 @@ rsc_action_digest(pe_resource_t *rsc, const char *task, const char *key, data = g_hash_table_lookup(node->details->digest_cache, key); if (data == NULL) { - data = pe__calculate_digests(rsc, task, key, node, xml_op, calc_secure, - data_set); + data = pe__calculate_digests(rsc, task, key, node, xml_op, NULL, + calc_secure, data_set); CRM_ASSERT(data != NULL); g_hash_table_insert(node->details->digest_cache, strdup(key), data); } -- 1.8.3.1