pacemaker  2.1.2-ada5c3b36
Scalable High-Availability cluster resource manager
pe_digest.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 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 substring of a given string
52 static bool
53 attr_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 not substring of a given string
68 static bool
69 attr_not_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 
83 #if ENABLE_VERSIONED_ATTRS
84 static void
85 append_versioned_params(xmlNode *versioned_params, const char *ra_version, xmlNode *params)
86 {
87  GHashTable *hash = pe_unpack_versioned_parameters(versioned_params, ra_version);
88  char *key = NULL;
89  char *value = NULL;
90  GHashTableIter iter;
91 
92  g_hash_table_iter_init(&iter, hash);
93  while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
94  crm_xml_add(params, key, value);
95  }
96  g_hash_table_destroy(hash);
97 }
98 
99 static void
100 append_all_versioned_params(pe_resource_t *rsc, pe_node_t *node,
101  pe_action_t *action, xmlNode *xml_op,
102  pe_working_set_t *data_set)
103 {
104  const char *ra_version = NULL;
105  xmlNode *local_versioned_params = NULL;
106  pe_rsc_action_details_t *details = pe_rsc_action_details(action);
107 
108  local_versioned_params = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS);
109  pe_get_versioned_attributes(local_versioned_params, rsc, node, data_set);
110  if (xml_op != NULL) {
111  ra_version = crm_element_value(xml_op, XML_ATTR_RA_VERSION);
112  }
113  append_versioned_params(local_versioned_params, ra_version,
114  data->params_all);
115  append_versioned_params(rsc->versioned_parameters, ra_version,
116  data->params_all);
117  append_versioned_params(details->versioned_parameters, ra_version,
118  data->params_all);
119 }
120 #endif
121 
137 static void
138 calculate_main_digest(op_digest_cache_t *data, pe_resource_t *rsc,
139  pe_node_t *node, GHashTable *params,
140  const char *task, guint *interval_ms,
141  xmlNode *xml_op, const char *op_version,
142  GHashTable *overrides, pe_working_set_t *data_set)
143 {
144  pe_action_t *action = NULL;
145 
146  data->params_all = create_xml_node(NULL, XML_TAG_PARAMS);
147 
148  /* REMOTE_CONTAINER_HACK: Allow Pacemaker Remote nodes to run containers
149  * that themselves are Pacemaker Remote nodes
150  */
151  if (pe__add_bundle_remote_name(rsc, data_set, data->params_all,
153  crm_trace("Set address for bundle connection %s (on %s)",
154  rsc->id, node->details->uname);
155  }
156 
157  // If interval was overridden, reset it
158  if (overrides != NULL) {
159  const char *interval_s = g_hash_table_lookup(overrides, CRM_META "_"
161 
162  if (interval_s != NULL) {
163  long long value_ll;
164 
165  if ((pcmk__scan_ll(interval_s, &value_ll, 0LL) == pcmk_rc_ok)
166  && (value_ll >= 0) && (value_ll <= G_MAXUINT)) {
167  *interval_ms = (guint) value_ll;
168  }
169  }
170  }
171 
172  action = custom_action(rsc, pcmk__op_key(rsc->id, task, *interval_ms),
173  task, node, TRUE, FALSE, data_set);
174  if (overrides != NULL) {
175  g_hash_table_foreach(overrides, hash2field, data->params_all);
176  }
177  g_hash_table_foreach(params, hash2field, data->params_all);
178  g_hash_table_foreach(action->extra, hash2field, data->params_all);
179  g_hash_table_foreach(action->meta, hash2metafield, data->params_all);
180 
181 #if ENABLE_VERSIONED_ATTRS
182  append_all_versioned_params(rsc, node, action, xml_op, data_set);
183 #endif
184 
185  pcmk__filter_op_for_digest(data->params_all);
186 
188 
189  data->digest_all_calc = calculate_operation_digest(data->params_all,
190  op_version);
191 }
192 
193 // Return true if XML attribute name is a Pacemaker-defined fencing parameter
194 static bool
195 is_fence_param(xmlAttrPtr attr, void *user_data)
196 {
197  return pcmk_stonith_param((const char *) attr->name);
198 }
199 
211 static void
212 calculate_secure_digest(op_digest_cache_t *data, pe_resource_t *rsc,
213  GHashTable *params, xmlNode *xml_op,
214  const char *op_version, GHashTable *overrides)
215 {
216  const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
217  const char *secure_list = NULL;
218 
219  if (xml_op == NULL) {
220  secure_list = " passwd password user ";
221  } else {
222  secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE);
223  }
224 
225  data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS);
226  if (overrides != NULL) {
227  g_hash_table_foreach(overrides, hash2field, data->params_secure);
228  }
229 
230  g_hash_table_foreach(params, hash2field, data->params_secure);
231  if (secure_list != NULL) {
232  pcmk__xe_remove_matching_attrs(data->params_secure, attr_not_in_string,
233  (void *) secure_list);
234  }
235  if (pcmk_is_set(pcmk_get_ra_caps(class),
237  /* For stonith resources, Pacemaker adds special parameters,
238  * but these are not listed in fence agent meta-data, so the
239  * controller will not hash them. That means we have to filter
240  * them out before calculating our hash for comparison.
241  */
242  pcmk__xe_remove_matching_attrs(data->params_secure, is_fence_param,
243  NULL);
244  }
245  pcmk__filter_op_for_digest(data->params_secure);
246 
247  /* CRM_meta_timeout *should* be part of a digest for recurring operations.
248  * However, currently the controller does not add timeout to secure digests,
249  * because it only includes parameters declared by the resource agent.
250  * Remove any timeout that made it this far, to match.
251  *
252  * @TODO Update the controller to add the timeout (which will require
253  * bumping the feature set and checking that here).
254  */
255  xml_remove_prop(data->params_secure, CRM_META "_" XML_ATTR_TIMEOUT);
256 
257  data->digest_secure_calc = calculate_operation_digest(data->params_secure,
258  op_version);
259 }
260 
272 static void
273 calculate_restart_digest(op_digest_cache_t *data, xmlNode *xml_op,
274  const char *op_version)
275 {
276  const char *value = NULL;
277 
278  // We must have XML of resource operation history
279  if (xml_op == NULL) {
280  return;
281  }
282 
283  // And the history must have a restart digest to compare against
284  if (crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) == NULL) {
285  return;
286  }
287 
288  // Start with a copy of all parameters
289  data->params_restart = copy_xml(data->params_all);
290 
291  // Then filter out reloadable parameters, if any
293  if (value != NULL) {
294  pcmk__xe_remove_matching_attrs(data->params_restart, attr_in_string,
295  (void *) value);
296  }
297 
298  value = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
299  data->digest_restart_calc = calculate_operation_digest(data->params_restart,
300  value);
301 }
302 
321 pe__calculate_digests(pe_resource_t *rsc, const char *task, guint *interval_ms,
322  pe_node_t *node, xmlNode *xml_op, GHashTable *overrides,
323  bool calc_secure, pe_working_set_t *data_set)
324 {
325  op_digest_cache_t *data = calloc(1, sizeof(op_digest_cache_t));
326  const char *op_version = CRM_FEATURE_SET;
327  GHashTable *params = NULL;
328 
329  if (data == NULL) {
330  return NULL;
331  }
332  if (xml_op != NULL) {
333  op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
334  }
335 
336  params = pe_rsc_params(rsc, node, data_set);
337  calculate_main_digest(data, rsc, node, params, task, interval_ms, xml_op,
338  op_version, overrides, data_set);
339  if (calc_secure) {
340  calculate_secure_digest(data, rsc, params, xml_op, op_version,
341  overrides);
342  }
343  calculate_restart_digest(data, xml_op, op_version);
344  return data;
345 }
346 
361 static op_digest_cache_t *
362 rsc_action_digest(pe_resource_t *rsc, const char *task, guint interval_ms,
363  pe_node_t *node, xmlNode *xml_op, bool calc_secure,
364  pe_working_set_t *data_set)
365 {
366  op_digest_cache_t *data = NULL;
367  char *key = pcmk__op_key(rsc->id, task, interval_ms);
368 
369  data = g_hash_table_lookup(node->details->digest_cache, key);
370  if (data == NULL) {
371  data = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op,
372  NULL, calc_secure, data_set);
373  CRM_ASSERT(data != NULL);
374  g_hash_table_insert(node->details->digest_cache, strdup(key), data);
375  }
376  free(key);
377  return data;
378 }
379 
392 rsc_action_digest_cmp(pe_resource_t * rsc, xmlNode * xml_op, pe_node_t * node,
393  pe_working_set_t * data_set)
394 {
395  op_digest_cache_t *data = NULL;
396  guint interval_ms = 0;
397 
398  const char *op_version;
399  const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
400  const char *digest_all;
401  const char *digest_restart;
402 
403  CRM_ASSERT(node != NULL);
404 
405  op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
406  digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
407  digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
408 
409  crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
410  data = rsc_action_digest(rsc, task, interval_ms, node, xml_op,
411  pcmk_is_set(data_set->flags, pe_flag_sanitized),
412  data_set);
413 
414  data->rc = RSC_DIGEST_MATCH;
415  if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) {
416  pe_rsc_info(rsc, "Parameters to %ums-interval %s action for %s on %s "
417  "changed: hash was %s vs. now %s (restart:%s) %s",
418  interval_ms, task, rsc->id, node->details->uname,
419  crm_str(digest_restart), data->digest_restart_calc,
420  op_version,
422  data->rc = RSC_DIGEST_RESTART;
423 
424  } else if (digest_all == NULL) {
425  /* it is unknown what the previous op digest was */
426  data->rc = RSC_DIGEST_UNKNOWN;
427 
428  } else if (strcmp(digest_all, data->digest_all_calc) != 0) {
429  pe_rsc_info(rsc, "Parameters to %ums-interval %s action for %s on %s "
430  "changed: hash was %s vs. now %s (%s:%s) %s",
431  interval_ms, task, rsc->id, node->details->uname,
432  crm_str(digest_all), data->digest_all_calc,
433  (interval_ms > 0)? "reschedule" : "reload",
434  op_version,
436  data->rc = RSC_DIGEST_ALL;
437  }
438  return data;
439 }
440 
458 static inline char *
459 create_unfencing_summary(const char *rsc_id, const char *agent_type,
460  const char *param_digest)
461 {
462  return crm_strdup_printf("%s:%s:%s", rsc_id, agent_type, param_digest);
463 }
464 
481 static bool
482 unfencing_digest_matches(const char *rsc_id, const char *agent,
483  const char *digest_calc, const char *node_summary)
484 {
485  bool matches = FALSE;
486 
487  if (rsc_id && agent && digest_calc && node_summary) {
488  char *search_secure = create_unfencing_summary(rsc_id, agent,
489  digest_calc);
490 
491  /* The digest was calculated including the device ID and agent,
492  * so there is no risk of collision using strstr().
493  */
494  matches = (strstr(node_summary, search_secure) != NULL);
495  crm_trace("Calculated unfencing digest '%s' %sfound in '%s'",
496  search_secure, matches? "" : "not ", node_summary);
497  free(search_secure);
498  }
499  return matches;
500 }
501 
502 /* Magic string to use as action name for digest cache entries used for
503  * unfencing checks. This is not a real action name (i.e. "on"), so
504  * check_action_definition() won't confuse these entries with real actions.
505  */
506 #define STONITH_DIGEST_TASK "stonith-on"
507 
521  pe_node_t *node, pe_working_set_t *data_set)
522 {
523  const char *node_summary = NULL;
524 
525  // Calculate device's current parameter digests
526  op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, 0U,
527  node, NULL, TRUE, data_set);
528 
529  // Check whether node has special unfencing summary node attribute
530  node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL);
531  if (node_summary == NULL) {
532  data->rc = RSC_DIGEST_UNKNOWN;
533  return data;
534  }
535 
536  // Check whether full parameter digest matches
537  if (unfencing_digest_matches(rsc->id, agent, data->digest_all_calc,
538  node_summary)) {
539  data->rc = RSC_DIGEST_MATCH;
540  return data;
541  }
542 
543  // Check whether secure parameter digest matches
544  node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE);
545  if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc,
546  node_summary)) {
547  data->rc = RSC_DIGEST_MATCH;
548  if (!pcmk__is_daemon && data_set->priv != NULL) {
549  pcmk__output_t *out = data_set->priv;
550  out->info(out, "Only 'private' parameters to %s "
551  "for unfencing %s changed", rsc->id,
552  node->details->uname);
553  }
554  return data;
555  }
556 
557  // Parameters don't match
558  data->rc = RSC_DIGEST_ALL;
559  if (pcmk_is_set(data_set->flags, pe_flag_sanitized) && data->digest_secure_calc) {
560  if (data_set->priv != NULL) {
561  pcmk__output_t *out = data_set->priv;
562  char *digest = create_unfencing_summary(rsc->id, agent,
563  data->digest_secure_calc);
564 
565  out->info(out, "Parameters to %s for unfencing "
566  "%s changed, try '%s'", rsc->id,
567  node->details->uname, digest);
568  free(digest);
569  } else if (!pcmk__is_daemon) {
570  char *digest = create_unfencing_summary(rsc->id, agent,
571  data->digest_secure_calc);
572 
573  printf("Parameters to %s for unfencing %s changed, try '%s'\n",
574  rsc->id, node->details->uname, digest);
575  free(digest);
576  }
577  }
578  return data;
579 }
A dumping ground.
char data[0]
Definition: cpg.c:55
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:785
#define XML_ATTR_TRANSITION_MAGIC
Definition: msg_xml.h:398
op_digest_cache_t * rsc_action_digest_cmp(pe_resource_t *rsc, xmlNode *xml_op, pe_node_t *node, pe_working_set_t *data_set)
Definition: pe_digest.c:392
#define CRM_FEATURE_SET
Definition: crm.h:69
bool pcmk_stonith_param(const char *param)
Check whether a given stonith parameter is handled by Pacemaker.
Definition: agents.c:170
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:291
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:310
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:123
#define XML_LRM_ATTR_OP_RESTART
Definition: msg_xml.h:311
#define CRM_ATTR_DIGESTS_ALL
Definition: crm.h:122
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: operations.c:390
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
#define XML_RSC_ATTR_REMOTE_RA_ADDR
Definition: msg_xml.h:247
const char * action
Definition: pcmk_fence.c:30
#define XML_LRM_ATTR_OP_SECURE
Definition: msg_xml.h:312
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:830
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:297
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:170
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:622
#define pe_flag_sanitized
Definition: pe_types.h:120
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:629
#define crm_trace(fmt, args...)
Definition: logging.h:363
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:114
struct pe_node_shared_s * details
Definition: pe_types.h:244
const char * uname
Definition: pe_types.h:209
GHashTable * pe_rsc_params(pe_resource_t *rsc, pe_node_t *node, pe_working_set_t *data_set)
Get a table of resource parameters.
Definition: complex.c:457
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:696
#define CRM_ATTR_DIGESTS_SECURE
Definition: crm.h:123
const char * pe_node_attribute_raw(pe_node_t *node, const char *name)
Definition: common.c:635
#define XML_LRM_ATTR_RESTART_DIGEST
Definition: msg_xml.h:313
#define XML_TAG_RSC_VER_ATTRS
Definition: msg_xml.h:206
void free_xml(xmlNode *child)
Definition: xml.c:824
op_digest_cache_t * pe__compare_fencing_digest(pe_resource_t *rsc, const char *agent, pe_node_t *node, pe_working_set_t *data_set)
Definition: pe_digest.c:520
op_digest_cache_t * pe__calculate_digests(pe_resource_t *rsc, const char *task, guint *interval_ms, pe_node_t *node, xmlNode *xml_op, GHashTable *overrides, bool calc_secure, pe_working_set_t *data_set)
Definition: pe_digest.c:321
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: operations.c:45
const char * pe__add_bundle_remote_name(pe_resource_t *rsc, pe_working_set_t *data_set, xmlNode *xml, const char *field)
Definition: bundle.c:961
#define CRM_META
Definition: crm.h:78
#define CRM_ASSERT(expr)
Definition: results.h:42
#define STONITH_DIGEST_TASK
Definition: pe_digest.c:506
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2032
This structure contains everything that makes up a single output formatter.
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:295
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition: nvpair.c:813
#define crm_str(x)
Definition: logging.h:383
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:112
GHashTable * digest_cache
cache of calculated resource digests
Definition: pe_types.h:236
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
unsigned long long flags
Definition: pe_types.h:146
char * name
Definition: pcmk_fence.c:31
#define XML_TAG_PARAMS
Definition: msg_xml.h:209
#define XML_ATTR_RA_VERSION
Definition: msg_xml.h:115
void pe_free_action(pe_action_t *action)
Definition: utils.c:1456
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:18
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:266
char * id
Definition: pe_types.h:322
pe_action_t * custom_action(pe_resource_t *rsc, char *key, const char *task, pe_node_t *on_node, gboolean optional, gboolean foo, pe_working_set_t *data_set)
Create or update an action object.
Definition: utils.c:731