pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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>
20#include "pe_status_private.h"
21
22extern bool pcmk__is_daemon;
23
33void
34pe__free_digests(gpointer ptr)
35{
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
52static bool
53attr_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
68static bool
69attr_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
84static void
85append_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
99static void
100append_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
137static void
138calculate_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
194static bool
195is_fence_param(xmlAttrPtr attr, void *user_data)
196{
197 return pcmk_stonith_param((const char *) attr->name);
198}
199
211static void
212calculate_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
272static void
273calculate_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
299 data->digest_restart_calc = calculate_operation_digest(data->params_restart,
300 value);
301}
302
321pe__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
361static op_digest_cache_t *
362rsc_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
392rsc_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,
412 data_set);
413
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,
423
424 } else if (digest_all == NULL) {
425 /* it is unknown what the previous op digest was */
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
458static inline char *
459create_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
481static bool
482unfencing_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) {
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)) {
540 return data;
541 }
542
543 // Check whether secure parameter digest matches
545 if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc,
546 node_summary)) {
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}
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition agents.c:31
bool pcmk_stonith_param(const char *param)
Check whether a given stonith parameter is handled by Pacemaker.
Definition agents.c:170
@ pcmk_ra_cap_fence_params
Definition agents.h:53
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition operations.c:390
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
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
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
char data[0]
Definition cpg.c:10
A dumping ground.
#define CRM_ATTR_DIGESTS_ALL
Definition crm.h:122
#define CRM_FEATURE_SET
Definition crm.h:69
#define CRM_META
Definition crm.h:78
#define CRM_ATTR_DIGESTS_SECURE
Definition crm.h:123
#define crm_str(x)
Definition logging.h:376
#define crm_trace(fmt, args...)
Definition logging.h:356
#define XML_ATTR_CRM_VERSION
Definition msg_xml.h:112
#define XML_LRM_ATTR_OP_DIGEST
Definition msg_xml.h:310
#define XML_TAG_RSC_VER_ATTRS
Definition msg_xml.h:206
#define XML_LRM_ATTR_OP_SECURE
Definition msg_xml.h:312
#define XML_LRM_ATTR_INTERVAL
Definition msg_xml.h:291
#define XML_LRM_ATTR_OP_RESTART
Definition msg_xml.h:311
#define XML_ATTR_TRANSITION_MAGIC
Definition msg_xml.h:398
#define XML_LRM_ATTR_RESTART_DIGEST
Definition msg_xml.h:313
#define XML_AGENT_ATTR_CLASS
Definition msg_xml.h:266
#define XML_LRM_ATTR_TASK
Definition msg_xml.h:297
#define XML_ATTR_TIMEOUT
Definition msg_xml.h:123
#define XML_RSC_ATTR_REMOTE_RA_ADDR
Definition msg_xml.h:247
#define XML_TAG_PARAMS
Definition msg_xml.h:209
#define XML_LRM_ATTR_INTERVAL_MS
Definition msg_xml.h:295
#define XML_ATTR_RA_VERSION
Definition msg_xml.h:115
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:530
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition nvpair.c:814
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition nvpair.c:786
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition nvpair.c:623
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:324
char * name
Definition pcmk_fence.c:31
const char * action
Definition pcmk_fence.c:30
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
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 STONITH_DIGEST_TASK
Definition pe_digest.c:506
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
void pe__free_digests(gpointer ptr)
Definition pe_digest.c:34
bool pcmk__is_daemon
Definition logging.c:47
#define pe_flag_sanitized
Definition pe_types.h:120
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)
Definition utils.c:415
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:968
const char * pe_node_attribute_raw(pe_node_t *node, const char *name)
Definition common.c:635
void pe_free_action(pe_action_t *action)
Definition utils.c:1344
@ RSC_DIGEST_ALL
Definition internal.h:471
@ RSC_DIGEST_UNKNOWN
Definition internal.h:474
@ RSC_DIGEST_RESTART
Definition internal.h:469
@ RSC_DIGEST_MATCH
Definition internal.h:467
#define pe_rsc_info(rsc, fmt, args...)
Definition internal.h:18
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:142
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:97
This structure contains everything that makes up a single output formatter.
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
struct pe_node_shared_s * details
Definition pe_types.h:244
GHashTable * digest_cache
cache of calculated resource digests
Definition pe_types.h:236
const char * uname
Definition pe_types.h:209
unsigned long long flags
Definition pe_types.h:146
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition xml.c:823
xmlNode * copy_xml(xmlNode *src_node)
Definition xml.c:829
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition xml.c:696
void xml_remove_prop(xmlNode *obj, const char *name)
Definition xml.c:2036
char * calculate_operation_digest(xmlNode *local_cib, const char *version)
Calculate and return digest of XML operation.
Definition digest.c:170
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition xml.c:630