pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
pe_digest.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <glib.h>
13 #include <stdbool.h>
14 
15 #include <crm/crm.h>
16 #include <crm/msg_xml.h>
17 #include <crm/common/xml.h>
19 #include <crm/pengine/internal.h>
20 #include "pe_status_private.h"
21 
22 extern bool pcmk__is_daemon;
23 
33 void
34 pe__free_digests(gpointer ptr)
35 {
36  op_digest_cache_t *data = ptr;
37 
38  if (data != NULL) {
39  free_xml(data->params_all);
40  free_xml(data->params_secure);
41  free_xml(data->params_restart);
42 
43  free(data->digest_all_calc);
44  free(data->digest_restart_calc);
45  free(data->digest_secure_calc);
46 
47  free(data);
48  }
49 }
50 
51 // Return true if XML attribute name is not substring of a given string
52 static bool
53 attr_not_in_string(xmlAttrPtr a, void *user_data)
54 {
55  bool filter = false;
56  char *name = crm_strdup_printf(" %s ", (const char *) a->name);
57 
58  if (strstr((const char *) user_data, name) == NULL) {
59  crm_trace("Filtering %s (not found in '%s')",
60  (const char *) a->name, (const char *) user_data);
61  filter = true;
62  }
63  free(name);
64  return filter;
65 }
66 
67 // Return true if XML attribute name is substring of a given string
68 static bool
69 attr_in_string(xmlAttrPtr a, void *user_data)
70 {
71  bool filter = false;
72  char *name = crm_strdup_printf(" %s ", (const char *) a->name);
73 
74  if (strstr((const char *) user_data, name) != NULL) {
75  crm_trace("Filtering %s (found in '%s')",
76  (const char *) a->name, (const char *) user_data);
77  filter = true;
78  }
79  free(name);
80  return filter;
81 }
82 
98 static void
99 calculate_main_digest(op_digest_cache_t *data, pcmk_resource_t *rsc,
100  const pcmk_node_t *node, GHashTable *params,
101  const char *task, guint *interval_ms,
102  const xmlNode *xml_op, const char *op_version,
103  GHashTable *overrides, pcmk_scheduler_t *scheduler)
104 {
105  xmlNode *action_config = NULL;
106 
107  data->params_all = create_xml_node(NULL, XML_TAG_PARAMS);
108 
109  /* REMOTE_CONTAINER_HACK: Allow Pacemaker Remote nodes to run containers
110  * that themselves are Pacemaker Remote nodes
111  */
112  (void) pe__add_bundle_remote_name(rsc, scheduler, data->params_all,
114 
115  if (overrides != NULL) {
116  // If interval was overridden, reset it
117  const char *interval_s = g_hash_table_lookup(overrides, CRM_META "_"
119 
120  if (interval_s != NULL) {
121  long long value_ll;
122 
123  if ((pcmk__scan_ll(interval_s, &value_ll, 0LL) == pcmk_rc_ok)
124  && (value_ll >= 0) && (value_ll <= G_MAXUINT)) {
125  *interval_ms = (guint) value_ll;
126  }
127  }
128 
129  // Add overrides to list of all parameters
130  g_hash_table_foreach(overrides, hash2field, data->params_all);
131  }
132 
133  // Add provided instance parameters
134  g_hash_table_foreach(params, hash2field, data->params_all);
135 
136  // Find action configuration XML in CIB
137  action_config = pcmk__find_action_config(rsc, task, *interval_ms, true);
138 
139  /* Add action-specific resource instance attributes to the digest list.
140  *
141  * If this is a one-time action with action-specific instance attributes,
142  * enforce a restart instead of reload-agent in case the main digest doesn't
143  * match, even if the restart digest does. This ensures any changes of the
144  * action-specific parameters get applied for this specific action, and
145  * digests calculated for the resulting history will be correct. Default the
146  * result to RSC_DIGEST_RESTART for the case where the main digest doesn't
147  * match.
148  */
149  params = pcmk__unpack_action_rsc_params(action_config, node->details->attrs,
150  scheduler);
151  if ((*interval_ms == 0) && (g_hash_table_size(params) > 0)) {
153  }
154  g_hash_table_foreach(params, hash2field, data->params_all);
155  g_hash_table_destroy(params);
156 
157  // Add action meta-attributes
158  params = pcmk__unpack_action_meta(rsc, node, task, *interval_ms,
159  action_config);
160  g_hash_table_foreach(params, hash2metafield, data->params_all);
161  g_hash_table_destroy(params);
162 
163  pcmk__filter_op_for_digest(data->params_all);
164 
165  data->digest_all_calc = calculate_operation_digest(data->params_all,
166  op_version);
167 }
168 
169 // Return true if XML attribute name is a Pacemaker-defined fencing parameter
170 static bool
171 is_fence_param(xmlAttrPtr attr, void *user_data)
172 {
173  return pcmk_stonith_param((const char *) attr->name);
174 }
175 
187 static void
188 calculate_secure_digest(op_digest_cache_t *data, const pcmk_resource_t *rsc,
189  GHashTable *params, const xmlNode *xml_op,
190  const char *op_version, GHashTable *overrides)
191 {
192  const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
193  const char *secure_list = NULL;
194  bool old_version = (compare_version(op_version, "3.16.0") < 0);
195 
196  if (xml_op == NULL) {
197  secure_list = " passwd password user ";
198  } else {
199  secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE);
200  }
201 
202  if (old_version) {
203  data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS);
204  if (overrides != NULL) {
205  g_hash_table_foreach(overrides, hash2field, data->params_secure);
206  }
207 
208  g_hash_table_foreach(params, hash2field, data->params_secure);
209 
210  } else {
211  // Start with a copy of all parameters
212  data->params_secure = copy_xml(data->params_all);
213  }
214 
215  if (secure_list != NULL) {
216  pcmk__xe_remove_matching_attrs(data->params_secure, attr_in_string,
217  (void *) secure_list);
218  }
219  if (old_version
220  && pcmk_is_set(pcmk_get_ra_caps(class),
222  /* For stonith resources, Pacemaker adds special parameters,
223  * but these are not listed in fence agent meta-data, so with older
224  * versions of DC, the controller will not hash them. That means we have
225  * to filter them out before calculating our hash for comparison.
226  */
227  pcmk__xe_remove_matching_attrs(data->params_secure, is_fence_param,
228  NULL);
229  }
230  pcmk__filter_op_for_digest(data->params_secure);
231 
232  /* CRM_meta_timeout *should* be part of a digest for recurring operations.
233  * However, with older versions of DC, the controller does not add timeout
234  * to secure digests, because it only includes parameters declared by the
235  * resource agent.
236  * Remove any timeout that made it this far, to match.
237  */
238  if (old_version) {
239  xml_remove_prop(data->params_secure, CRM_META "_" XML_ATTR_TIMEOUT);
240  }
241 
242  data->digest_secure_calc = calculate_operation_digest(data->params_secure,
243  op_version);
244 }
245 
257 static void
258 calculate_restart_digest(op_digest_cache_t *data, const xmlNode *xml_op,
259  const char *op_version)
260 {
261  const char *value = NULL;
262 
263  // We must have XML of resource operation history
264  if (xml_op == NULL) {
265  return;
266  }
267 
268  // And the history must have a restart digest to compare against
269  if (crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) == NULL) {
270  return;
271  }
272 
273  // Start with a copy of all parameters
274  data->params_restart = copy_xml(data->params_all);
275 
276  // Then filter out reloadable parameters, if any
278  if (value != NULL) {
279  pcmk__xe_remove_matching_attrs(data->params_restart, attr_not_in_string,
280  (void *) value);
281  }
282 
283  value = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
284  data->digest_restart_calc = calculate_operation_digest(data->params_restart,
285  value);
286 }
287 
306 pe__calculate_digests(pcmk_resource_t *rsc, const char *task,
307  guint *interval_ms, const pcmk_node_t *node,
308  const xmlNode *xml_op, GHashTable *overrides,
309  bool calc_secure, pcmk_scheduler_t *scheduler)
310 {
311  op_digest_cache_t *data = calloc(1, sizeof(op_digest_cache_t));
312  const char *op_version = NULL;
313  GHashTable *params = NULL;
314 
315  if (data == NULL) {
316  return NULL;
317  }
318 
319  data->rc = pcmk__digest_match;
320 
321  if (xml_op != NULL) {
322  op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
323  }
324 
325  if (op_version == NULL && scheduler != NULL && scheduler->input != NULL) {
327  }
328 
329  if (op_version == NULL) {
330  op_version = CRM_FEATURE_SET;
331  }
332 
333  params = pe_rsc_params(rsc, node, scheduler);
334  calculate_main_digest(data, rsc, node, params, task, interval_ms, xml_op,
335  op_version, overrides, scheduler);
336  if (calc_secure) {
337  calculate_secure_digest(data, rsc, params, xml_op, op_version,
338  overrides);
339  }
340  calculate_restart_digest(data, xml_op, op_version);
341  return data;
342 }
343 
358 static op_digest_cache_t *
359 rsc_action_digest(pcmk_resource_t *rsc, const char *task, guint interval_ms,
360  pcmk_node_t *node, const xmlNode *xml_op,
361  bool calc_secure, pcmk_scheduler_t *scheduler)
362 {
363  op_digest_cache_t *data = NULL;
364  char *key = pcmk__op_key(rsc->id, task, interval_ms);
365 
366  data = g_hash_table_lookup(node->details->digest_cache, key);
367  if (data == NULL) {
368  data = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op,
369  NULL, calc_secure, scheduler);
370  CRM_ASSERT(data != NULL);
371  g_hash_table_insert(node->details->digest_cache, strdup(key), data);
372  }
373  free(key);
374  return data;
375 }
376 
389 rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op,
391 {
392  op_digest_cache_t *data = NULL;
393  guint interval_ms = 0;
394 
395  const char *op_version;
396  const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
397  const char *digest_all;
398  const char *digest_restart;
399 
400  CRM_ASSERT(node != NULL);
401 
402  op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
403  digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
404  digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
405 
406  crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
407  data = rsc_action_digest(rsc, task, interval_ms, node, xml_op,
410  scheduler);
411 
412  if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) {
413  pe_rsc_info(rsc, "Parameters to %ums-interval %s action for %s on %s "
414  "changed: hash was %s vs. now %s (restart:%s) %s",
415  interval_ms, task, rsc->id, pe__node_name(node),
416  pcmk__s(digest_restart, "missing"),
417  data->digest_restart_calc,
418  op_version,
421 
422  } else if (digest_all == NULL) {
423  /* it is unknown what the previous op digest was */
425 
426  } else if (strcmp(digest_all, data->digest_all_calc) != 0) {
427  /* Given a non-recurring operation with extra parameters configured,
428  * in case that the main digest doesn't match, even if the restart
429  * digest matches, enforce a restart rather than a reload-agent anyway.
430  * So that it ensures any changes of the extra parameters get applied
431  * for this specific operation, and the digests calculated for the
432  * resulting lrm_rsc_op will be correct.
433  * Preserve the implied rc pcmk__digest_restart for the case that the
434  * main digest doesn't match.
435  */
436  if ((interval_ms == 0) && (data->rc == pcmk__digest_restart)) {
437  pe_rsc_info(rsc, "Parameters containing extra ones to %ums-interval"
438  " %s action for %s on %s "
439  "changed: hash was %s vs. now %s (restart:%s) %s",
440  interval_ms, task, rsc->id, pe__node_name(node),
441  pcmk__s(digest_all, "missing"), data->digest_all_calc,
442  op_version,
444 
445  } else {
446  pe_rsc_info(rsc, "Parameters to %ums-interval %s action for %s on %s "
447  "changed: hash was %s vs. now %s (%s:%s) %s",
448  interval_ms, task, rsc->id, pe__node_name(node),
449  pcmk__s(digest_all, "missing"), data->digest_all_calc,
450  (interval_ms > 0)? "reschedule" : "reload",
451  op_version,
454  }
455 
456  } else {
457  data->rc = pcmk__digest_match;
458  }
459  return data;
460 }
461 
479 static inline char *
480 create_unfencing_summary(const char *rsc_id, const char *agent_type,
481  const char *param_digest)
482 {
483  return crm_strdup_printf("%s:%s:%s", rsc_id, agent_type, param_digest);
484 }
485 
502 static bool
503 unfencing_digest_matches(const char *rsc_id, const char *agent,
504  const char *digest_calc, const char *node_summary)
505 {
506  bool matches = FALSE;
507 
508  if (rsc_id && agent && digest_calc && node_summary) {
509  char *search_secure = create_unfencing_summary(rsc_id, agent,
510  digest_calc);
511 
512  /* The digest was calculated including the device ID and agent,
513  * so there is no risk of collision using strstr().
514  */
515  matches = (strstr(node_summary, search_secure) != NULL);
516  crm_trace("Calculated unfencing digest '%s' %sfound in '%s'",
517  search_secure, matches? "" : "not ", node_summary);
518  free(search_secure);
519  }
520  return matches;
521 }
522 
523 /* Magic string to use as action name for digest cache entries used for
524  * unfencing checks. This is not a real action name (i.e. "on"), so
525  * pcmk__check_action_config() won't confuse these entries with real actions.
526  */
527 #define STONITH_DIGEST_TASK "stonith-on"
528 
543 {
544  const char *node_summary = NULL;
545 
546  // Calculate device's current parameter digests
547  op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, 0U,
548  node, NULL, TRUE, scheduler);
549 
550  // Check whether node has special unfencing summary node attribute
551  node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL);
552  if (node_summary == NULL) {
554  return data;
555  }
556 
557  // Check whether full parameter digest matches
558  if (unfencing_digest_matches(rsc->id, agent, data->digest_all_calc,
559  node_summary)) {
560  data->rc = pcmk__digest_match;
561  return data;
562  }
563 
564  // Check whether secure parameter digest matches
565  node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE);
566  if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc,
567  node_summary)) {
568  data->rc = pcmk__digest_match;
569  if (!pcmk__is_daemon && scheduler->priv != NULL) {
570  pcmk__output_t *out = scheduler->priv;
571  out->info(out, "Only 'private' parameters to %s "
572  "for unfencing %s changed", rsc->id,
573  pe__node_name(node));
574  }
575  return data;
576  }
577 
578  // Parameters don't match
581  && (data->digest_secure_calc != NULL)) {
582 
583  if (scheduler->priv != NULL) {
584  pcmk__output_t *out = scheduler->priv;
585  char *digest = create_unfencing_summary(rsc->id, agent,
586  data->digest_secure_calc);
587 
588  out->info(out, "Parameters to %s for unfencing "
589  "%s changed, try '%s'", rsc->id,
590  pe__node_name(node), digest);
591  free(digest);
592  } else if (!pcmk__is_daemon) {
593  char *digest = create_unfencing_summary(rsc->id, agent,
594  data->digest_secure_calc);
595 
596  printf("Parameters to %s for unfencing %s changed, try '%s'\n",
597  rsc->id, pe__node_name(node), digest);
598  free(digest);
599  }
600  }
601  return data;
602 }
A dumping ground.
GHashTable * attrs
Node attributes.
Definition: nodes.h:115
char data[0]
Definition: cpg.c:55
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: actions.c:344
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:700
#define XML_ATTR_TRANSITION_MAGIC
Definition: msg_xml.h:415
const char * pe_node_attribute_raw(const pcmk_node_t *node, const char *name)
Definition: common.c:621
const char * name
Definition: cib.c:26
#define CRM_FEATURE_SET
Definition: crm.h:70
bool pcmk_stonith_param(const char *param)
Check whether a given stonith parameter is handled by Pacemaker.
Definition: agents.c:174
GHashTable * pcmk__unpack_action_rsc_params(const xmlNode *action_xml, GHashTable *node_attrs, pcmk_scheduler_t *data_set)
Definition: pe_actions.c:240
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:300
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:319
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:150
#define XML_LRM_ATTR_OP_RESTART
Definition: msg_xml.h:320
#define CRM_ATTR_DIGESTS_ALL
Definition: crm.h:121
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Implementation of pcmk_scheduler_t.
Definition: scheduler.h:172
#define XML_RSC_ATTR_REMOTE_RA_ADDR
Definition: msg_xml.h:261
#define XML_LRM_ATTR_OP_SECURE
Definition: msg_xml.h:321
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:789
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:306
op_digest_cache_t * pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Definition: pe_digest.c:541
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
char * calculate_operation_digest(xmlNode *local_cib, const char *version)
Calculate and return digest of XML operation.
Definition: digest.c:150
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:540
Implementation of pcmk_resource_t.
Definition: resources.h:399
const char * pe__add_bundle_remote_name(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, xmlNode *xml, const char *field)
Definition: bundle.c:938
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:593
#define crm_trace(fmt, args...)
Definition: logging.h:387
void * priv
For Pacemaker use only.
Definition: scheduler.h:229
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:99
struct pe_node_shared_s * details
Basic node information.
Definition: nodes.h:134
Wrappers for and extensions to libxml2.
bool pcmk__is_daemon
Definition: logging.c:47
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
#define CRM_ATTR_DIGESTS_SECURE
Definition: crm.h:122
#define XML_LRM_ATTR_RESTART_DIGEST
Definition: msg_xml.h:322
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: actions.c:42
void free_xml(xmlNode *child)
Definition: xml.c:783
Implementation of pcmk_node_t.
Definition: nodes.h:130
xmlNode * input
CIB XML.
Definition: scheduler.h:175
Whether sensitive resource attributes have been masked.
Definition: scheduler.h:146
xmlNode * pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, bool include_disabled)
Definition: pe_actions.c:129
#define CRM_META
Definition: crm.h:79
pcmk_scheduler_t * scheduler
#define CRM_ASSERT(expr)
Definition: results.h:42
#define STONITH_DIGEST_TASK
Definition: pe_digest.c:527
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:1696
This structure contains everything that makes up a single output formatter.
int compare_version(const char *version1, const char *version2)
Definition: utils.c:189
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:304
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition: nvpair.c:728
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:140
GHashTable * digest_cache
Cache of calculated resource digests.
Definition: nodes.h:117
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
void pe__free_digests(gpointer ptr)
Definition: pe_digest.c:34
op_digest_cache_t * pe__calculate_digests(pcmk_resource_t *rsc, const char *task, guint *interval_ms, const pcmk_node_t *node, const xmlNode *xml_op, GHashTable *overrides, bool calc_secure, pcmk_scheduler_t *scheduler)
Definition: pe_digest.c:306
GHashTable * pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Get a table of resource parameters.
Definition: complex.c:446
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition: scheduler.h:183
op_digest_cache_t * rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op, pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Definition: pe_digest.c:389
#define XML_TAG_PARAMS
Definition: msg_xml.h:230
GHashTable * pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, const char *action_name, guint interval_ms, const xmlNode *action_config)
Definition: pe_actions.c:692
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:35
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:280
char * id
Resource ID in configuration.
Definition: resources.h:400