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