From 591324bc6f15c12bf1547c7c20e8e99da219f1fc Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
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 <kgaillot@redhat.com>
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 <kgaillot@redhat.com>
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 <crm/pengine/rules.h>
#include <crm/pengine/internal.h>
+#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 <kgaillot@redhat.com>
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 <crm_internal.h>
+
+#include <glib.h>
+#include <stdbool.h>
+
+#include <crm/crm.h>
+#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
+#include <crm/pengine/internal.h>
+#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 <crm/pengine/rules.h>
#include <crm/pengine/internal.h>
-
#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 <kgaillot@redhat.com>
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