ff725c
From 28566d6832274c59f27bb7b2f1f54420a3f3d822 Mon Sep 17 00:00:00 2001
ff725c
From: Ken Gaillot <kgaillot@redhat.com>
ff725c
Date: Thu, 9 May 2019 20:26:08 -0500
ff725c
Subject: [PATCH 1/2] Refactor: libpe_status: functionize unfencing digest code
ff725c
 more
ff725c
ff725c
... for readability, reusability, and avoiding unnecessary function calls or
ff725c
memory allocation.
ff725c
---
ff725c
 lib/pengine/utils.c | 159 ++++++++++++++++++++++++++++++++++++++--------------
ff725c
 1 file changed, 118 insertions(+), 41 deletions(-)
ff725c
ff725c
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
ff725c
index 2f4dc1e..f80f8d4 100644
ff725c
--- a/lib/pengine/utils.c
ff725c
+++ b/lib/pengine/utils.c
ff725c
@@ -2080,57 +2080,134 @@ rsc_action_digest_cmp(resource_t * rsc, xmlNode * xml_op, node_t * node,
ff725c
     return data;
ff725c
 }
ff725c
 
ff725c
+/*!
ff725c
+ * \internal
ff725c
+ * \brief Create an unfencing summary for use in special node attribute
ff725c
+ *
ff725c
+ * Create a string combining a fence device's resource ID, agent type, and
ff725c
+ * parameter digest (whether for all parameters or just non-private parameters).
ff725c
+ * This can be stored in a special node attribute, allowing us to detect changes
ff725c
+ * in either the agent type or parameters, to know whether unfencing must be
ff725c
+ * redone or can be safely skipped when the device's history is cleaned.
ff725c
+ *
ff725c
+ * \param[in] rsc_id        Fence device resource ID
ff725c
+ * \param[in] agent_type    Fence device agent
ff725c
+ * \param[in] param_digest  Fence device parameter digest
ff725c
+ *
ff725c
+ * \return Newly allocated string with unfencing digest
ff725c
+ * \note The caller is responsible for freeing the result.
ff725c
+ */
ff725c
+static inline char *
ff725c
+create_unfencing_summary(const char *rsc_id, const char *agent_type,
ff725c
+                         const char *param_digest)
ff725c
+{
ff725c
+    return crm_strdup_printf("%s:%s:%s", rsc_id, agent_type, param_digest);
ff725c
+}
ff725c
+
ff725c
+/*!
ff725c
+ * \internal
ff725c
+ * \brief Check whether a node can skip unfencing
ff725c
+ *
ff725c
+ * Check whether a fence device's current definition matches a node's
ff725c
+ * stored summary of when it was last unfenced by the device.
ff725c
+ *
ff725c
+ * \param[in] rsc_id        Fence device's resource ID
ff725c
+ * \param[in] agent         Fence device's agent type
ff725c
+ * \param[in] digest_calc   Fence device's current parameter digest
ff725c
+ * \param[in] node_summary  Value of node's special unfencing node attribute
ff725c
+ *                          (a comma-separated list of unfencing summaries for
ff725c
+ *                          all devices that have unfenced this node)
ff725c
+ *
ff725c
+ * \return TRUE if digest matches, FALSE otherwise
ff725c
+ */
ff725c
+static bool
ff725c
+unfencing_digest_matches(const char *rsc_id, const char *agent,
ff725c
+                         const char *digest_calc, const char *node_summary)
ff725c
+{
ff725c
+    bool matches = FALSE;
ff725c
+
ff725c
+    if (rsc_id && agent && digest_calc && node_summary) {
ff725c
+        char *search_secure = create_unfencing_summary(rsc_id, agent,
ff725c
+                                                       digest_calc);
ff725c
+
ff725c
+        /* The digest was calculated including the device ID and agent,
ff725c
+         * so there is no risk of collision using strstr().
ff725c
+         */
ff725c
+        matches = (strstr(node_summary, search_secure) != NULL);
ff725c
+        crm_trace("Calculated unfencing digest '%s' %sfound in '%s'",
ff725c
+                  search_secure, matches? "" : "not ", node_summary);
ff725c
+        free(search_secure);
ff725c
+    }
ff725c
+    return matches;
ff725c
+}
ff725c
+
ff725c
+/* Magic string to use as action name for digest cache entries used for
ff725c
+ * unfencing checks. This is not a real action name (i.e. "on"), so
ff725c
+ * check_action_definition() won't confuse these entries with real actions.
ff725c
+ */
ff725c
 #define STONITH_DIGEST_TASK "stonith-on"
ff725c
 
ff725c
+/*!
ff725c
+ * \internal
ff725c
+ * \brief Calculate fence device digests and digest comparison result
ff725c
+ *
ff725c
+ * \param[in] rsc       Fence device resource
ff725c
+ * \param[in] agent     Fence device's agent type
ff725c
+ * \param[in] node      Node with digest cache to use
ff725c
+ * \param[in] data_set  Cluster working set
ff725c
+ *
ff725c
+ * \return Node's digest cache entry
ff725c
+ */
ff725c
 static op_digest_cache_t *
ff725c
-fencing_action_digest_cmp(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
ff725c
+fencing_action_digest_cmp(pe_resource_t *rsc, const char *agent,
ff725c
+                          pe_node_t *node, pe_working_set_t *data_set)
ff725c
 {
ff725c
-    char *key = generate_op_key(rsc->id, STONITH_DIGEST_TASK, 0);
ff725c
-    op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, key, node, NULL, data_set);
ff725c
+    const char *node_summary = NULL;
ff725c
 
ff725c
-    const char *digest_all = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL);
ff725c
-    const char *digest_secure = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE);
ff725c
+    // Calculate device's current parameter digests
ff725c
+    char *key = generate_op_key(rsc->id, STONITH_DIGEST_TASK, 0);
ff725c
+    op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, key,
ff725c
+                                                node, NULL, data_set);
ff725c
 
ff725c
-    /* No 'reloads' for fencing device changes
ff725c
-     *
ff725c
-     * We use the resource id + agent + digest so that we can detect
ff725c
-     * changes to the agent and/or the parameters used
ff725c
-     */
ff725c
-    char *search_all = crm_strdup_printf("%s:%s:%s", rsc->id, (const char*)g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE), data->digest_all_calc);
ff725c
-    char *search_secure = crm_strdup_printf("%s:%s:%s", rsc->id, (const char*)g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE), data->digest_secure_calc);
ff725c
+    free(key);
ff725c
 
ff725c
-    data->rc = RSC_DIGEST_ALL;
ff725c
-    if (digest_all == NULL) {
ff725c
-        /* it is unknown what the previous op digest was */
ff725c
+    // Check whether node has special unfencing summary node attribute
ff725c
+    node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL);
ff725c
+    if (node_summary == NULL) {
ff725c
         data->rc = RSC_DIGEST_UNKNOWN;
ff725c
+        return data;
ff725c
+    }
ff725c
 
ff725c
-    } else if (strstr(digest_all, search_all)) {
ff725c
+    // Check whether full parameter digest matches
ff725c
+    if (unfencing_digest_matches(rsc->id, agent, data->digest_all_calc,
ff725c
+                                 node_summary)) {
ff725c
         data->rc = RSC_DIGEST_MATCH;
ff725c
+        return data;
ff725c
+    }
ff725c
 
ff725c
-    } else if(digest_secure && data->digest_secure_calc) {
ff725c
-        if(strstr(digest_secure, search_secure)) {
ff725c
-            if (is_set(data_set->flags, pe_flag_stdout)) {
ff725c
-                printf("Only 'private' parameters to %s for unfencing %s changed\n",
ff725c
-                       rsc->id, node->details->uname);
ff725c
-            }
ff725c
-            data->rc = RSC_DIGEST_MATCH;
ff725c
+    // Check whether secure parameter digest matches
ff725c
+    node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE);
ff725c
+    if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc,
ff725c
+                                 node_summary)) {
ff725c
+        data->rc = RSC_DIGEST_MATCH;
ff725c
+        if (is_set(data_set->flags, pe_flag_stdout)) {
ff725c
+            printf("Only 'private' parameters to %s for unfencing %s changed\n",
ff725c
+                   rsc->id, node->details->uname);
ff725c
         }
ff725c
+        return data;
ff725c
     }
ff725c
 
ff725c
-    if (is_set(data_set->flags, pe_flag_sanitized)
ff725c
-        && is_set(data_set->flags, pe_flag_stdout)
ff725c
-        && (data->rc == RSC_DIGEST_ALL)
ff725c
+    // Parameters don't match
ff725c
+    data->rc = RSC_DIGEST_ALL;
ff725c
+    if (is_set(data_set->flags, (pe_flag_sanitized|pe_flag_stdout))
ff725c
         && data->digest_secure_calc) {
ff725c
-        printf("Parameters to %s for unfencing %s changed, try '%s:%s:%s'\n",
ff725c
-               rsc->id, node->details->uname, rsc->id,
ff725c
-               (const char *) g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE),
ff725c
-               data->digest_secure_calc);
ff725c
-    }
ff725c
-
ff725c
-    free(key);
ff725c
-    free(search_all);
ff725c
-    free(search_secure);
ff725c
+        char *digest = create_unfencing_summary(rsc->id, agent,
ff725c
+                                                data->digest_secure_calc);
ff725c
 
ff725c
+        printf("Parameters to %s for unfencing %s changed, try '%s'\n",
ff725c
+               rsc->id, node->details->uname, digest);
ff725c
+        free(digest);
ff725c
+    }
ff725c
     return data;
ff725c
 }
ff725c
 
ff725c
@@ -2218,9 +2295,6 @@ pe_fence_op(node_t * node, const char *op, bool optional, const char *reason, pe
ff725c
              *
ff725c
              * We may do this for all nodes in the future, but for now
ff725c
              * the check_action_definition() based stuff works fine.
ff725c
-             *
ff725c
-             * Use "stonith-on" to avoid creating cache entries for
ff725c
-             * operations check_action_definition() would look for.
ff725c
              */
ff725c
             long max = 1024;
ff725c
             long digests_all_offset = 0;
ff725c
@@ -2232,8 +2306,11 @@ pe_fence_op(node_t * node, const char *op, bool optional, const char *reason, pe
ff725c
 
ff725c
             for (GListPtr gIter = matches; gIter != NULL; gIter = gIter->next) {
ff725c
                 resource_t *match = gIter->data;
ff725c
-                op_digest_cache_t *data = fencing_action_digest_cmp(match, node, data_set);
ff725c
+                const char *agent = g_hash_table_lookup(match->meta,
ff725c
+                                                        XML_ATTR_TYPE);
ff725c
+                op_digest_cache_t *data = NULL;
ff725c
 
ff725c
+                data = fencing_action_digest_cmp(match, agent, node, data_set);
ff725c
                 if(data->rc == RSC_DIGEST_ALL) {
ff725c
                     optional = FALSE;
ff725c
                     crm_notice("Unfencing %s (remote): because the definition of %s changed", node->details->uname, match->id);
ff725c
@@ -2244,11 +2321,11 @@ pe_fence_op(node_t * node, const char *op, bool optional, const char *reason, pe
ff725c
 
ff725c
                 digests_all_offset += snprintf(
ff725c
                     digests_all+digests_all_offset, max-digests_all_offset,
ff725c
-                    "%s:%s:%s,", match->id, (const char*)g_hash_table_lookup(match->meta, XML_ATTR_TYPE), data->digest_all_calc);
ff725c
+                    "%s:%s:%s,", match->id, agent, data->digest_all_calc);
ff725c
 
ff725c
                 digests_secure_offset += snprintf(
ff725c
                     digests_secure+digests_secure_offset, max-digests_secure_offset,
ff725c
-                    "%s:%s:%s,", match->id, (const char*)g_hash_table_lookup(match->meta, XML_ATTR_TYPE), data->digest_secure_calc);
ff725c
+                    "%s:%s:%s,", match->id, agent, data->digest_secure_calc);
ff725c
             }
ff725c
             g_hash_table_insert(stonith_op->meta,
ff725c
                                 strdup(XML_OP_ATTR_DIGESTS_ALL),
ff725c
-- 
ff725c
1.8.3.1
ff725c
ff725c
ff725c
From fd6e06ff419c95f4423202163d2d4dca3f03a4c5 Mon Sep 17 00:00:00 2001
ff725c
From: Ken Gaillot <kgaillot@redhat.com>
ff725c
Date: Fri, 10 May 2019 11:57:31 -0500
ff725c
Subject: [PATCH 2/2] Fix: libpe_status: calculate secure digests for unfencing
ff725c
 ops
ff725c
ff725c
The calculation of digests for detection of when unfencing is needed reused
ff725c
rsc_action_digest(). However that would only add secure digests when the
ff725c
pe_flag_sanitized flag was set, which is only set by crm_simulate, so secure
ff725c
digests would never be added in normal cluster operation. This led to
ff725c
node attributes like name="#digests-secure"
ff725c
value="stonith-fence_compute-fence-nova:fence_compute:(null),".
ff725c
ff725c
Now, rsc_action_digest() takes a new argument to select whether secure digests
ff725c
are added, which is always set to TRUE when calculating unfencing digests.
ff725c
---
ff725c
 lib/pengine/utils.c | 27 ++++++++++++++++++++++-----
ff725c
 1 file changed, 22 insertions(+), 5 deletions(-)
ff725c
ff725c
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
ff725c
index f80f8d4..5b893f7 100644
ff725c
--- a/lib/pengine/utils.c
ff725c
+++ b/lib/pengine/utils.c
ff725c
@@ -1936,9 +1936,24 @@ append_versioned_params(xmlNode *versioned_params, const char *ra_version, xmlNo
ff725c
 }
ff725c
 #endif
ff725c
 
ff725c
+/*!
ff725c
+ * \internal
ff725c
+ * \brief Calculate action digests and store in node's digest cache
ff725c
+ *
ff725c
+ * \param[in] rsc          Resource that action was for
ff725c
+ * \param[in] task         Name of action performed
ff725c
+ * \param[in] key          Action's task key
ff725c
+ * \param[in] node         Node action was performed on
ff725c
+ * \param[in] xml_op       XML of operation in CIB status (if available)
ff725c
+ * \param[in] calc_secure  Whether to calculate secure digest
ff725c
+ * \param[in] data_set     Cluster working set
ff725c
+ *
ff725c
+ * \return Pointer to node's digest cache entry
ff725c
+ */
ff725c
 static op_digest_cache_t *
ff725c
-rsc_action_digest(resource_t * rsc, const char *task, const char *key,
ff725c
-                  node_t * node, xmlNode * xml_op, pe_working_set_t * data_set) 
ff725c
+rsc_action_digest(pe_resource_t *rsc, const char *task, const char *key,
ff725c
+                  pe_node_t *node, xmlNode *xml_op, bool calc_secure,
ff725c
+                  pe_working_set_t *data_set)
ff725c
 {
ff725c
     op_digest_cache_t *data = NULL;
ff725c
 
ff725c
@@ -2007,7 +2022,7 @@ rsc_action_digest(resource_t * rsc, const char *task, const char *key,
ff725c
 
ff725c
         data->digest_all_calc = calculate_operation_digest(data->params_all, op_version);
ff725c
 
ff725c
-        if (is_set(data_set->flags, pe_flag_sanitized)) {
ff725c
+        if (calc_secure) {
ff725c
             data->params_secure = copy_xml(data->params_all);
ff725c
             if(secure_list) {
ff725c
                 filter_parameters(data->params_secure, secure_list, FALSE);
ff725c
@@ -2053,7 +2068,9 @@ rsc_action_digest_cmp(resource_t * rsc, xmlNode * xml_op, node_t * node,
ff725c
 
ff725c
     interval_ms = crm_parse_ms(interval_ms_s);
ff725c
     key = generate_op_key(rsc->id, task, interval_ms);
ff725c
-    data = rsc_action_digest(rsc, task, key, node, xml_op, data_set);
ff725c
+    data = rsc_action_digest(rsc, task, key, node, xml_op,
ff725c
+                             is_set(data_set->flags, pe_flag_sanitized),
ff725c
+                             data_set);
ff725c
 
ff725c
     data->rc = RSC_DIGEST_MATCH;
ff725c
     if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) {
ff725c
@@ -2167,7 +2184,7 @@ fencing_action_digest_cmp(pe_resource_t *rsc, const char *agent,
ff725c
     // Calculate device's current parameter digests
ff725c
     char *key = generate_op_key(rsc->id, STONITH_DIGEST_TASK, 0);
ff725c
     op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, key,
ff725c
-                                                node, NULL, data_set);
ff725c
+                                                node, NULL, TRUE, data_set);
ff725c
 
ff725c
     free(key);
ff725c
 
ff725c
-- 
ff725c
1.8.3.1
ff725c