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 #include <crm/crm.h>
12 #include <crm/msg_xml.h>
13 #include <crm/common/xml.h>
14 #include <crm/common/xml_internal.h>
15 #include <crm/common/util.h>
16
17 #include <glib.h>
18 #include <stdbool.h>
19
20 #include <crm/pengine/rules.h>
21 #include <crm/pengine/internal.h>
22 #include "pe_status_private.h"
23
24 extern bool pcmk__is_daemon;
25
26 extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
27 void print_str_str(gpointer key, gpointer value, gpointer user_data);
28 gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
29 static void unpack_operation(pe_action_t * action, xmlNode * xml_obj, pe_resource_t * container,
30 pe_working_set_t * data_set, guint interval_ms);
31 static xmlNode *find_rsc_op_entry_helper(pe_resource_t * rsc, const char *key,
32 gboolean include_disabled);
33
34 #if ENABLE_VERSIONED_ATTRS
35 pe_rsc_action_details_t *
pe_rsc_action_details(pe_action_t * action)36 pe_rsc_action_details(pe_action_t *action)
37 {
38 pe_rsc_action_details_t *details;
39
40 CRM_CHECK(action != NULL, return NULL);
41
42 if (action->action_details == NULL) {
43 action->action_details = calloc(1, sizeof(pe_rsc_action_details_t));
44 CRM_CHECK(action->action_details != NULL, return NULL);
45 }
46
47 details = (pe_rsc_action_details_t *) action->action_details;
48 if (details->versioned_parameters == NULL) {
49 details->versioned_parameters = create_xml_node(NULL,
50 XML_TAG_OP_VER_ATTRS);
51 }
52 if (details->versioned_meta == NULL) {
53 details->versioned_meta = create_xml_node(NULL, XML_TAG_OP_VER_META);
54 }
55 return details;
56 }
57
58 static void
pe_free_rsc_action_details(pe_action_t * action)59 pe_free_rsc_action_details(pe_action_t *action)
60 {
61 pe_rsc_action_details_t *details;
62
63 if ((action == NULL) || (action->action_details == NULL)) {
64 return;
65 }
66
67 details = (pe_rsc_action_details_t *) action->action_details;
68
69 if (details->versioned_parameters) {
70 free_xml(details->versioned_parameters);
71 }
72 if (details->versioned_meta) {
73 free_xml(details->versioned_meta);
74 }
75
76 action->action_details = NULL;
77 }
78 #endif
79
80 /*!
81 * \internal
82 * \brief Check whether we can fence a particular node
83 *
84 * \param[in] data_set Working set for cluster
85 * \param[in] node Name of node to check
86 *
87 * \return true if node can be fenced, false otherwise
88 */
89 bool
pe_can_fence(pe_working_set_t * data_set,pe_node_t * node)90 pe_can_fence(pe_working_set_t *data_set, pe_node_t *node)
91 {
92 if (pe__is_guest_node(node)) {
93 /* Guest nodes are fenced by stopping their container resource. We can
94 * do that if the container's host is either online or fenceable.
95 */
96 pe_resource_t *rsc = node->details->remote_rsc->container;
97
98 for (GList *n = rsc->running_on; n != NULL; n = n->next) {
99 pe_node_t *container_node = n->data;
100
101 if (!container_node->details->online
102 && !pe_can_fence(data_set, container_node)) {
103 return false;
104 }
105 }
106 return true;
107
108 } else if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
109 return false; /* Turned off */
110
111 } else if (!pcmk_is_set(data_set->flags, pe_flag_have_stonith_resource)) {
112 return false; /* No devices */
113
114 } else if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)) {
115 return true;
116
117 } else if (data_set->no_quorum_policy == no_quorum_ignore) {
118 return true;
119
120 } else if(node == NULL) {
121 return false;
122
123 } else if(node->details->online) {
124 crm_notice("We can fence %s without quorum because they're in our membership", node->details->uname);
125 return true;
126 }
127
128 crm_trace("Cannot fence %s", node->details->uname);
129 return false;
130 }
131
132 /*!
133 * \internal
134 * \brief Copy a node object
135 *
136 * \param[in] this_node Node object to copy
137 *
138 * \return Newly allocated shallow copy of this_node
139 * \note This function asserts on errors and is guaranteed to return non-NULL.
140 */
141 pe_node_t *
pe__copy_node(const pe_node_t * this_node)142 pe__copy_node(const pe_node_t *this_node)
143 {
144 pe_node_t *new_node = NULL;
145
146 CRM_ASSERT(this_node != NULL);
147
148 new_node = calloc(1, sizeof(pe_node_t));
149 CRM_ASSERT(new_node != NULL);
150
151 new_node->rsc_discover_mode = this_node->rsc_discover_mode;
152 new_node->weight = this_node->weight;
153 new_node->fixed = this_node->fixed;
154 new_node->details = this_node->details;
155
156 return new_node;
157 }
158
159 /* any node in list1 or list2 and not in the other gets a score of -INFINITY */
160 void
node_list_exclude(GHashTable * hash,GList * list,gboolean merge_scores)161 node_list_exclude(GHashTable * hash, GList *list, gboolean merge_scores)
162 {
163 GHashTable *result = hash;
164 pe_node_t *other_node = NULL;
165 GList *gIter = list;
166
167 GHashTableIter iter;
168 pe_node_t *node = NULL;
169
170 g_hash_table_iter_init(&iter, hash);
171 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
172
173 other_node = pe_find_node_id(list, node->details->id);
174 if (other_node == NULL) {
175 node->weight = -INFINITY;
176 } else if (merge_scores) {
177 node->weight = pe__add_scores(node->weight, other_node->weight);
178 }
179 }
180
181 for (; gIter != NULL; gIter = gIter->next) {
182 pe_node_t *node = (pe_node_t *) gIter->data;
183
184 other_node = pe_hash_table_lookup(result, node->details->id);
185
186 if (other_node == NULL) {
187 pe_node_t *new_node = pe__copy_node(node);
188
189 new_node->weight = -INFINITY;
190 g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
191 }
192 }
193 }
194
195 /*!
196 * \internal
197 * \brief Create a node hash table from a node list
198 *
199 * \param[in] list Node list
200 *
201 * \return Hash table equivalent of node list
202 */
203 GHashTable *
pe__node_list2table(GList * list)204 pe__node_list2table(GList *list)
205 {
206 GHashTable *result = NULL;
207
208 result = pcmk__strkey_table(NULL, free);
209 for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
210 pe_node_t *new_node = pe__copy_node((pe_node_t *) gIter->data);
211
212 g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
213 }
214 return result;
215 }
216
217 gint
sort_node_uname(gconstpointer a,gconstpointer b)218 sort_node_uname(gconstpointer a, gconstpointer b)
219 {
220 return pcmk__numeric_strcasecmp(((const pe_node_t *) a)->details->uname,
221 ((const pe_node_t *) b)->details->uname);
222 }
223
224 /*!
225 * \internal
226 * \brief Output node weights to stdout
227 *
228 * \param[in] rsc Use allowed nodes for this resource
229 * \param[in] comment Text description to prefix lines with
230 * \param[in] nodes If rsc is not specified, use these nodes
231 */
232 static void
pe__output_node_weights(pe_resource_t * rsc,const char * comment,GHashTable * nodes,pe_working_set_t * data_set)233 pe__output_node_weights(pe_resource_t *rsc, const char *comment,
234 GHashTable *nodes, pe_working_set_t *data_set)
235 {
236 pcmk__output_t *out = data_set->priv;
237 char score[128]; // Stack-allocated since this is called frequently
238
239 // Sort the nodes so the output is consistent for regression tests
240 GList *list = g_list_sort(g_hash_table_get_values(nodes), sort_node_uname);
241
242 for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
243 pe_node_t *node = (pe_node_t *) gIter->data;
244
245 score2char_stack(node->weight, score, sizeof(score));
246 out->message(out, "node-weight", rsc, comment, node->details->uname, score);
247 }
248 g_list_free(list);
249 }
250
251 /*!
252 * \internal
253 * \brief Log node weights at trace level
254 *
255 * \param[in] file Caller's filename
256 * \param[in] function Caller's function name
257 * \param[in] line Caller's line number
258 * \param[in] rsc Use allowed nodes for this resource
259 * \param[in] comment Text description to prefix lines with
260 * \param[in] nodes If rsc is not specified, use these nodes
261 */
262 static void
pe__log_node_weights(const char * file,const char * function,int line,pe_resource_t * rsc,const char * comment,GHashTable * nodes)263 pe__log_node_weights(const char *file, const char *function, int line,
264 pe_resource_t *rsc, const char *comment, GHashTable *nodes)
265 {
266 GHashTableIter iter;
267 pe_node_t *node = NULL;
268 char score[128]; // Stack-allocated since this is called frequently
269
270 // Don't waste time if we're not tracing at this point
271 pcmk__log_else(LOG_TRACE, return);
272
273 g_hash_table_iter_init(&iter, nodes);
274 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
275 score2char_stack(node->weight, score, sizeof(score));
276 if (rsc) {
277 qb_log_from_external_source(function, file,
278 "%s: %s allocation score on %s: %s",
279 LOG_TRACE, line, 0,
280 comment, rsc->id,
281 node->details->uname, score);
282 } else {
283 qb_log_from_external_source(function, file, "%s: %s = %s",
284 LOG_TRACE, line, 0,
285 comment, node->details->uname,
286 score);
287 }
288 }
289 }
290
291 /*!
292 * \internal
293 * \brief Log or output node weights
294 *
295 * \param[in] file Caller's filename
296 * \param[in] function Caller's function name
297 * \param[in] line Caller's line number
298 * \param[in] to_log Log if true, otherwise output
299 * \param[in] rsc Use allowed nodes for this resource
300 * \param[in] comment Text description to prefix lines with
301 * \param[in] nodes Use these nodes
302 */
303 void
pe__show_node_weights_as(const char * file,const char * function,int line,bool to_log,pe_resource_t * rsc,const char * comment,GHashTable * nodes,pe_working_set_t * data_set)304 pe__show_node_weights_as(const char *file, const char *function, int line,
305 bool to_log, pe_resource_t *rsc, const char *comment,
306 GHashTable *nodes, pe_working_set_t *data_set)
307 {
308 if (rsc != NULL && pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
309 // Don't show allocation scores for orphans
310 return;
311 }
312 if (nodes == NULL) {
313 // Nothing to show
314 return;
315 }
316
317 if (to_log) {
318 pe__log_node_weights(file, function, line, rsc, comment, nodes);
319 } else {
320 pe__output_node_weights(rsc, comment, nodes, data_set);
321 }
322
323 // If this resource has children, repeat recursively for each
324 if (rsc && rsc->children) {
325 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
326 pe_resource_t *child = (pe_resource_t *) gIter->data;
327
328 pe__show_node_weights_as(file, function, line, to_log, child,
329 comment, child->allowed_nodes, data_set);
330 }
331 }
332 }
333
334 gint
sort_rsc_index(gconstpointer a,gconstpointer b)335 sort_rsc_index(gconstpointer a, gconstpointer b)
336 {
337 const pe_resource_t *resource1 = (const pe_resource_t *)a;
338 const pe_resource_t *resource2 = (const pe_resource_t *)b;
339
340 if (a == NULL && b == NULL) {
341 return 0;
342 }
343 if (a == NULL) {
344 return 1;
345 }
346 if (b == NULL) {
347 return -1;
348 }
349
350 if (resource1->sort_index > resource2->sort_index) {
351 return -1;
352 }
353
354 if (resource1->sort_index < resource2->sort_index) {
355 return 1;
356 }
357
358 return 0;
359 }
360
361 gint
sort_rsc_priority(gconstpointer a,gconstpointer b)362 sort_rsc_priority(gconstpointer a, gconstpointer b)
363 {
364 const pe_resource_t *resource1 = (const pe_resource_t *)a;
365 const pe_resource_t *resource2 = (const pe_resource_t *)b;
366
367 if (a == NULL && b == NULL) {
368 return 0;
369 }
370 if (a == NULL) {
371 return 1;
372 }
373 if (b == NULL) {
374 return -1;
375 }
376
377 if (resource1->priority > resource2->priority) {
378 return -1;
379 }
380
381 if (resource1->priority < resource2->priority) {
382 return 1;
383 }
384
385 return 0;
386 }
387
388 static enum pe_quorum_policy
effective_quorum_policy(pe_resource_t * rsc,pe_working_set_t * data_set)389 effective_quorum_policy(pe_resource_t *rsc, pe_working_set_t *data_set)
390 {
391 enum pe_quorum_policy policy = data_set->no_quorum_policy;
392
393 if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)) {
394 policy = no_quorum_ignore;
395
396 } else if (data_set->no_quorum_policy == no_quorum_demote) {
397 switch (rsc->role) {
398 case RSC_ROLE_PROMOTED:
399 case RSC_ROLE_UNPROMOTED:
400 if (rsc->next_role > RSC_ROLE_UNPROMOTED) {
401 pe__set_next_role(rsc, RSC_ROLE_UNPROMOTED,
402 "no-quorum-policy=demote");
403 }
404 policy = no_quorum_ignore;
405 break;
406 default:
407 policy = no_quorum_stop;
408 break;
409 }
410 }
411 return policy;
412 }
413
414 pe_action_t *
custom_action(pe_resource_t * rsc,char * key,const char * task,pe_node_t * on_node,gboolean optional,gboolean save_action,pe_working_set_t * data_set)415 custom_action(pe_resource_t * rsc, char *key, const char *task,
416 pe_node_t * on_node, gboolean optional, gboolean save_action,
417 pe_working_set_t * data_set)
418 {
419 pe_action_t *action = NULL;
420 GList *possible_matches = NULL;
421
422 CRM_CHECK(key != NULL, return NULL);
423 CRM_CHECK(task != NULL, free(key); return NULL);
424
425 if (save_action && rsc != NULL) {
426 possible_matches = find_actions(rsc->actions, key, on_node);
427 } else if(save_action) {
428 #if 0
429 action = g_hash_table_lookup(data_set->singletons, key);
430 #else
431 /* More expensive but takes 'node' into account */
432 possible_matches = find_actions(data_set->actions, key, on_node);
433 #endif
434 }
435
436 if(data_set->singletons == NULL) {
437 data_set->singletons = pcmk__strkey_table(NULL, NULL);
438 }
439
440 if (possible_matches != NULL) {
441 if (pcmk__list_of_multiple(possible_matches)) {
442 pe_warn("Action %s for %s on %s exists %d times",
443 task, rsc ? rsc->id : "<NULL>",
444 on_node ? on_node->details->uname : "<NULL>", g_list_length(possible_matches));
445 }
446
447 action = g_list_nth_data(possible_matches, 0);
448 pe_rsc_trace(rsc, "Found action %d: %s for %s (%s) on %s",
449 action->id, task, (rsc? rsc->id : "no resource"),
450 action->uuid,
451 (on_node? on_node->details->uname : "no node"));
452 g_list_free(possible_matches);
453 }
454
455 if (action == NULL) {
456 if (save_action) {
457 pe_rsc_trace(rsc, "Creating action %d (%s): %s for %s (%s) on %s",
458 data_set->action_id,
459 (optional? "optional" : "required"),
460 task, (rsc? rsc->id : "no resource"), key,
461 (on_node? on_node->details->uname : "no node"));
462 }
463
464 action = calloc(1, sizeof(pe_action_t));
465 if (save_action) {
466 action->id = data_set->action_id++;
467 } else {
468 action->id = 0;
469 }
470 action->rsc = rsc;
471 action->task = strdup(task);
472 if (on_node) {
473 action->node = pe__copy_node(on_node);
474 }
475 action->uuid = strdup(key);
476
477 if (pcmk__str_eq(task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
478 // Resource history deletion for a node can be done on the DC
479 pe__set_action_flags(action, pe_action_dc);
480 }
481
482 pe__set_action_flags(action, pe_action_runnable);
483 if (optional) {
484 pe__set_action_flags(action, pe_action_optional);
485 } else {
486 pe__clear_action_flags(action, pe_action_optional);
487 }
488
489 action->extra = pcmk__strkey_table(free, free);
490 action->meta = pcmk__strkey_table(free, free);
491
492 if (save_action) {
493 data_set->actions = g_list_prepend(data_set->actions, action);
494 if(rsc == NULL) {
495 g_hash_table_insert(data_set->singletons, action->uuid, action);
496 }
497 }
498
499 if (rsc != NULL) {
500 guint interval_ms = 0;
501
502 action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
503 parse_op_key(key, NULL, NULL, &interval_ms);
504
505 unpack_operation(action, action->op_entry, rsc->container, data_set,
506 interval_ms);
507
508 if (save_action) {
509 rsc->actions = g_list_prepend(rsc->actions, action);
510 }
511 }
512 }
513
514 if (!optional && pcmk_is_set(action->flags, pe_action_optional)) {
515 pe__clear_action_flags(action, pe_action_optional);
516 }
517
518 if (rsc != NULL) {
519 enum action_tasks a_task = text2task(action->task);
520 enum pe_quorum_policy quorum_policy = effective_quorum_policy(rsc, data_set);
521 int warn_level = LOG_TRACE;
522
523 if (save_action) {
524 warn_level = LOG_WARNING;
525 }
526
527 if (!pcmk_is_set(action->flags, pe_action_have_node_attrs)
528 && action->node != NULL && action->op_entry != NULL) {
529 pe_rule_eval_data_t rule_data = {
530 .node_hash = action->node->details->attrs,
531 .role = RSC_ROLE_UNKNOWN,
532 .now = data_set->now,
533 .match_data = NULL,
534 .rsc_data = NULL,
535 .op_data = NULL
536 };
537
538 pe__set_action_flags(action, pe_action_have_node_attrs);
539 pe__unpack_dataset_nvpairs(action->op_entry, XML_TAG_ATTR_SETS,
540 &rule_data, action->extra, NULL,
541 FALSE, data_set);
542 }
543
544 // Make the action optional if its resource is unmanaged
545 if (!pcmk_is_set(action->flags, pe_action_pseudo)
546 && (action->node != NULL)
547 && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)
548 && (g_hash_table_lookup(action->meta,
549 XML_LRM_ATTR_INTERVAL_MS) == NULL)) {
550 pe_rsc_debug(rsc, "%s on %s is optional (%s is unmanaged)",
551 action->uuid, action->node->details->uname,
552 action->rsc->id);
553 pe__set_action_flags(action, pe_action_optional);
554 // We shouldn't clear runnable here because ... something
555 }
556
557 // Make the action runnable or unrunnable as appropriate
558 if (pcmk_is_set(action->flags, pe_action_pseudo)) {
559 /* leave untouched */
560
561 } else if (action->node == NULL) {
562 pe_rsc_trace(rsc, "%s is unrunnable (unallocated)",
563 action->uuid);
564 pe__clear_action_flags(action, pe_action_runnable);
565
566 } else if (!pcmk_is_set(action->flags, pe_action_dc)
567 && !(action->node->details->online)
568 && (!pe__is_guest_node(action->node)
569 || action->node->details->remote_requires_reset)) {
570 pe__clear_action_flags(action, pe_action_runnable);
571 do_crm_log(warn_level,
572 "%s on %s is unrunnable (node is offline)",
573 action->uuid, action->node->details->uname);
574 if (pcmk_is_set(action->rsc->flags, pe_rsc_managed)
575 && save_action && a_task == stop_rsc
576 && action->node->details->unclean == FALSE) {
577 pe_fence_node(data_set, action->node, "resource actions are unrunnable", FALSE);
578 }
579
580 } else if (!pcmk_is_set(action->flags, pe_action_dc)
581 && action->node->details->pending) {
582 pe__clear_action_flags(action, pe_action_runnable);
583 do_crm_log(warn_level,
584 "Action %s on %s is unrunnable (node is pending)",
585 action->uuid, action->node->details->uname);
586
587 } else if (action->needs == rsc_req_nothing) {
588 pe_action_set_reason(action, NULL, TRUE);
589 if (pe__is_guest_node(action->node)
590 && !pe_can_fence(data_set, action->node)) {
591 /* An action that requires nothing usually does not require any
592 * fencing in order to be runnable. However, there is an
593 * exception: an action cannot be completed if it is on a guest
594 * node whose host is unclean and cannot be fenced.
595 */
596 pe_rsc_debug(rsc, "%s on %s is unrunnable "
597 "(node's host cannot be fenced)",
598 action->uuid, action->node->details->uname);
599 pe__clear_action_flags(action, pe_action_runnable);
600 } else {
601 pe_rsc_trace(rsc, "%s on %s does not require fencing or quorum",
602 action->uuid, action->node->details->uname);
603 pe__set_action_flags(action, pe_action_runnable);
604 }
605 #if 0
606 /*
607 * No point checking this
608 * - if we don't have quorum we can't stonith anyway
609 */
610 } else if (action->needs == rsc_req_stonith) {
611 crm_trace("Action %s requires only stonith", action->uuid);
612 action->runnable = TRUE;
613 #endif
614 } else if (quorum_policy == no_quorum_stop) {
615 pe_rsc_debug(rsc, "%s on %s is unrunnable (no quorum)",
616 action->uuid, action->node->details->uname);
617 pe_action_set_flag_reason(__func__, __LINE__, action, NULL,
618 "no quorum", pe_action_runnable, TRUE);
619
620 } else if (quorum_policy == no_quorum_freeze) {
621 if (rsc->fns->active(rsc, TRUE) == FALSE || rsc->next_role > rsc->role) {
622 pe_rsc_debug(rsc, "%s on %s is unrunnable (no quorum)",
623 action->uuid, action->node->details->uname);
624 pe_action_set_flag_reason(__func__, __LINE__, action, NULL,
625 "quorum freeze", pe_action_runnable,
626 TRUE);
627 }
628
629 } else {
630 //pe_action_set_reason(action, NULL, TRUE);
631 pe__set_action_flags(action, pe_action_runnable);
632 }
633
634 if (save_action) {
635 switch (a_task) {
636 case stop_rsc:
637 pe__set_resource_flags(rsc, pe_rsc_stopping);
638 break;
639 case start_rsc:
640 pe__clear_resource_flags(rsc, pe_rsc_starting);
641 if (pcmk_is_set(action->flags, pe_action_runnable)) {
642 pe__set_resource_flags(rsc, pe_rsc_starting);
643 }
644 break;
645 default:
646 break;
647 }
648 }
649 }
650
651 free(key);
652 return action;
653 }
654
655 static bool
valid_stop_on_fail(const char * value)656 valid_stop_on_fail(const char *value)
657 {
658 return !pcmk__strcase_any_of(value, "standby", "demote", "stop", NULL);
659 }
660
661 static const char *
unpack_operation_on_fail(pe_action_t * action)662 unpack_operation_on_fail(pe_action_t * action)
663 {
664
665 const char *name = NULL;
666 const char *role = NULL;
667 const char *on_fail = NULL;
668 const char *interval_spec = NULL;
669 const char *enabled = NULL;
670 const char *value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
671
672 if (pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)
673 && !valid_stop_on_fail(value)) {
674
675 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s stop "
676 "action to default value because '%s' is not "
677 "allowed for stop", action->rsc->id, value);
678 return NULL;
679
680 } else if (pcmk__str_eq(action->task, CRMD_ACTION_DEMOTE, pcmk__str_casei) && !value) {
681 // demote on_fail defaults to monitor value for promoted role if present
682 xmlNode *operation = NULL;
683
684 CRM_CHECK(action->rsc != NULL, return NULL);
685
686 for (operation = pcmk__xe_first_child(action->rsc->ops_xml);
687 (operation != NULL) && (value == NULL);
688 operation = pcmk__xe_next(operation)) {
689
690 if (!pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
691 continue;
692 }
693 name = crm_element_value(operation, "name");
694 role = crm_element_value(operation, "role");
695 on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
696 enabled = crm_element_value(operation, "enabled");
697 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
698 if (!on_fail) {
699 continue;
700 } else if (enabled && !crm_is_true(enabled)) {
701 continue;
702 } else if (!pcmk__str_eq(name, "monitor", pcmk__str_casei)
703 || !pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
704 RSC_ROLE_PROMOTED_LEGACY_S,
705 NULL)) {
706 continue;
707 } else if (crm_parse_interval_spec(interval_spec) == 0) {
708 continue;
709 } else if (pcmk__str_eq(on_fail, "demote", pcmk__str_casei)) {
710 continue;
711 }
712
713 value = on_fail;
714 }
715 } else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
716 value = "ignore";
717
718 } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
719 name = crm_element_value(action->op_entry, "name");
720 role = crm_element_value(action->op_entry, "role");
721 interval_spec = crm_element_value(action->op_entry,
722 XML_LRM_ATTR_INTERVAL);
723
724 if (!pcmk__str_eq(name, CRMD_ACTION_PROMOTE, pcmk__str_casei)
725 && (!pcmk__str_eq(name, CRMD_ACTION_STATUS, pcmk__str_casei)
726 || !pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
727 RSC_ROLE_PROMOTED_LEGACY_S, NULL)
728 || (crm_parse_interval_spec(interval_spec) == 0))) {
729 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s %s "
730 "action to default value because 'demote' is not "
731 "allowed for it", action->rsc->id, name);
732 return NULL;
733 }
734 }
735
736 return value;
737 }
738
739 static xmlNode *
find_min_interval_mon(pe_resource_t * rsc,gboolean include_disabled)740 find_min_interval_mon(pe_resource_t * rsc, gboolean include_disabled)
741 {
742 guint interval_ms = 0;
743 guint min_interval_ms = G_MAXUINT;
744 const char *name = NULL;
745 const char *value = NULL;
746 const char *interval_spec = NULL;
747 xmlNode *op = NULL;
748 xmlNode *operation = NULL;
749
750 for (operation = pcmk__xe_first_child(rsc->ops_xml);
751 operation != NULL;
752 operation = pcmk__xe_next(operation)) {
753
754 if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
755 name = crm_element_value(operation, "name");
756 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
757 value = crm_element_value(operation, "enabled");
758 if (!include_disabled && value && crm_is_true(value) == FALSE) {
759 continue;
760 }
761
762 if (!pcmk__str_eq(name, RSC_STATUS, pcmk__str_casei)) {
763 continue;
764 }
765
766 interval_ms = crm_parse_interval_spec(interval_spec);
767
768 if (interval_ms && (interval_ms < min_interval_ms)) {
769 min_interval_ms = interval_ms;
770 op = operation;
771 }
772 }
773 }
774
775 return op;
776 }
777
778 static int
unpack_start_delay(const char * value,GHashTable * meta)779 unpack_start_delay(const char *value, GHashTable *meta)
780 {
781 int start_delay = 0;
782
783 if (value != NULL) {
784 start_delay = crm_get_msec(value);
785
786 if (start_delay < 0) {
787 start_delay = 0;
788 }
789
790 if (meta) {
791 g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
792 pcmk__itoa(start_delay));
793 }
794 }
795
796 return start_delay;
797 }
798
799 // true if value contains valid, non-NULL interval origin for recurring op
800 static bool
unpack_interval_origin(const char * value,xmlNode * xml_obj,guint interval_ms,crm_time_t * now,long long * start_delay)801 unpack_interval_origin(const char *value, xmlNode *xml_obj, guint interval_ms,
802 crm_time_t *now, long long *start_delay)
803 {
804 long long result = 0;
805 guint interval_sec = interval_ms / 1000;
806 crm_time_t *origin = NULL;
807
808 // Ignore unspecified values and non-recurring operations
809 if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
810 return false;
811 }
812
813 // Parse interval origin from text
814 origin = crm_time_new(value);
815 if (origin == NULL) {
816 pcmk__config_err("Ignoring '" XML_OP_ATTR_ORIGIN "' for operation "
817 "'%s' because '%s' is not valid",
818 (ID(xml_obj)? ID(xml_obj) : "(missing ID)"), value);
819 return false;
820 }
821
822 // Get seconds since origin (negative if origin is in the future)
823 result = crm_time_get_seconds(now) - crm_time_get_seconds(origin);
824 crm_time_free(origin);
825
826 // Calculate seconds from closest interval to now
827 result = result % interval_sec;
828
829 // Calculate seconds remaining until next interval
830 result = ((result <= 0)? 0 : interval_sec) - result;
831 crm_info("Calculated a start delay of %llds for operation '%s'",
832 result,
833 (ID(xml_obj)? ID(xml_obj) : "(unspecified)"));
834
835 if (start_delay != NULL) {
836 *start_delay = result * 1000; // milliseconds
837 }
838 return true;
839 }
840
841 static int
unpack_timeout(const char * value)842 unpack_timeout(const char *value)
843 {
844 int timeout_ms = crm_get_msec(value);
845
846 if (timeout_ms < 0) {
847 timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
848 }
849 return timeout_ms;
850 }
851
852 int
pe_get_configured_timeout(pe_resource_t * rsc,const char * action,pe_working_set_t * data_set)853 pe_get_configured_timeout(pe_resource_t *rsc, const char *action, pe_working_set_t *data_set)
854 {
855 xmlNode *child = NULL;
856 GHashTable *action_meta = NULL;
857 const char *timeout_spec = NULL;
858 int timeout_ms = 0;
859
860 pe_rule_eval_data_t rule_data = {
861 .node_hash = NULL,
862 .role = RSC_ROLE_UNKNOWN,
863 .now = data_set->now,
864 .match_data = NULL,
865 .rsc_data = NULL,
866 .op_data = NULL
867 };
868
869 for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
870 child != NULL; child = crm_next_same_xml(child)) {
871 if (pcmk__str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME),
872 pcmk__str_casei)) {
873 timeout_spec = crm_element_value(child, XML_ATTR_TIMEOUT);
874 break;
875 }
876 }
877
878 if (timeout_spec == NULL && data_set->op_defaults) {
879 action_meta = pcmk__strkey_table(free, free);
880 pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS,
881 &rule_data, action_meta, NULL, FALSE, data_set);
882 timeout_spec = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
883 }
884
885 // @TODO check meta-attributes (including versioned meta-attributes)
886 // @TODO maybe use min-interval monitor timeout as default for monitors
887
888 timeout_ms = crm_get_msec(timeout_spec);
889 if (timeout_ms < 0) {
890 timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
891 }
892
893 if (action_meta != NULL) {
894 g_hash_table_destroy(action_meta);
895 }
896 return timeout_ms;
897 }
898
899 #if ENABLE_VERSIONED_ATTRS
900 static void
unpack_versioned_meta(xmlNode * versioned_meta,xmlNode * xml_obj,guint interval_ms,crm_time_t * now)901 unpack_versioned_meta(xmlNode *versioned_meta, xmlNode *xml_obj,
902 guint interval_ms, crm_time_t *now)
903 {
904 xmlNode *attrs = NULL;
905 xmlNode *attr = NULL;
906
907 for (attrs = pcmk__xe_first_child(versioned_meta); attrs != NULL;
908 attrs = pcmk__xe_next(attrs)) {
909
910 for (attr = pcmk__xe_first_child(attrs); attr != NULL;
911 attr = pcmk__xe_next(attr)) {
912
913 const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
914 const char *value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
915
916 if (pcmk__str_eq(name, XML_OP_ATTR_START_DELAY, pcmk__str_casei)) {
917 int start_delay = unpack_start_delay(value, NULL);
918
919 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
920 } else if (pcmk__str_eq(name, XML_OP_ATTR_ORIGIN, pcmk__str_casei)) {
921 long long start_delay = 0;
922
923 if (unpack_interval_origin(value, xml_obj, interval_ms, now,
924 &start_delay)) {
925 crm_xml_add(attr, XML_NVPAIR_ATTR_NAME,
926 XML_OP_ATTR_START_DELAY);
927 crm_xml_add_ll(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
928 }
929 } else if (pcmk__str_eq(name, XML_ATTR_TIMEOUT, pcmk__str_casei)) {
930 int timeout_ms = unpack_timeout(value);
931
932 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, timeout_ms);
933 }
934 }
935 }
936 }
937 #endif
938
939 /*!
940 * \brief Unpack operation XML into an action structure
941 *
942 * Unpack an operation's meta-attributes (normalizing the interval, timeout,
943 * and start delay values as integer milliseconds), requirements, and
944 * failure policy.
945 *
946 * \param[in,out] action Action to unpack into
947 * \param[in] xml_obj Operation XML (or NULL if all defaults)
948 * \param[in] container Resource that contains affected resource, if any
949 * \param[in] data_set Cluster state
950 * \param[in] interval_ms How frequently to perform the operation
951 */
952 static void
unpack_operation(pe_action_t * action,xmlNode * xml_obj,pe_resource_t * container,pe_working_set_t * data_set,guint interval_ms)953 unpack_operation(pe_action_t * action, xmlNode * xml_obj, pe_resource_t * container,
954 pe_working_set_t * data_set, guint interval_ms)
955 {
956 int timeout_ms = 0;
957 const char *value = NULL;
958 bool is_probe = pcmk__str_eq(action->task, RSC_STATUS, pcmk__str_casei)
959 && (interval_ms == 0);
960 #if ENABLE_VERSIONED_ATTRS
961 pe_rsc_action_details_t *rsc_details = NULL;
962 #endif
963
964 pe_rsc_eval_data_t rsc_rule_data = {
965 .standard = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_CLASS),
966 .provider = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_PROVIDER),
967 .agent = crm_element_value(action->rsc->xml, XML_EXPR_ATTR_TYPE)
968 };
969
970 pe_op_eval_data_t op_rule_data = {
971 .op_name = action->task,
972 .interval = interval_ms
973 };
974
975 pe_rule_eval_data_t rule_data = {
976 .node_hash = NULL,
977 .role = RSC_ROLE_UNKNOWN,
978 .now = data_set->now,
979 .match_data = NULL,
980 .rsc_data = &rsc_rule_data,
981 .op_data = &op_rule_data
982 };
983
984 CRM_CHECK(action && action->rsc, return);
985
986 // Cluster-wide <op_defaults> <meta_attributes>
987 pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS, &rule_data,
988 action->meta, NULL, FALSE, data_set);
989
990 // Determine probe default timeout differently
991 if (is_probe) {
992 xmlNode *min_interval_mon = find_min_interval_mon(action->rsc, FALSE);
993
994 if (min_interval_mon) {
995 value = crm_element_value(min_interval_mon, XML_ATTR_TIMEOUT);
996 if (value) {
997 crm_trace("\t%s: Setting default timeout to minimum-interval "
998 "monitor's timeout '%s'", action->uuid, value);
999 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1000 strdup(value));
1001 }
1002 }
1003 }
1004
1005 if (xml_obj) {
1006 xmlAttrPtr xIter = NULL;
1007
1008 // <op> <meta_attributes> take precedence over defaults
1009 pe__unpack_dataset_nvpairs(xml_obj, XML_TAG_META_SETS, &rule_data,
1010 action->meta, NULL, TRUE, data_set);
1011
1012 #if ENABLE_VERSIONED_ATTRS
1013 rsc_details = pe_rsc_action_details(action);
1014
1015 pe_eval_versioned_attributes(data_set->input, xml_obj,
1016 XML_TAG_ATTR_SETS, &rule_data,
1017 rsc_details->versioned_parameters,
1018 NULL);
1019 pe_eval_versioned_attributes(data_set->input, xml_obj,
1020 XML_TAG_META_SETS, &rule_data,
1021 rsc_details->versioned_meta,
1022 NULL);
1023 #endif
1024
1025 /* Anything set as an <op> XML property has highest precedence.
1026 * This ensures we use the name and interval from the <op> tag.
1027 */
1028 for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
1029 const char *prop_name = (const char *)xIter->name;
1030 const char *prop_value = crm_element_value(xml_obj, prop_name);
1031
1032 g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value));
1033 }
1034 }
1035
1036 g_hash_table_remove(action->meta, "id");
1037
1038 // Normalize interval to milliseconds
1039 if (interval_ms > 0) {
1040 g_hash_table_replace(action->meta, strdup(XML_LRM_ATTR_INTERVAL),
1041 crm_strdup_printf("%u", interval_ms));
1042 } else {
1043 g_hash_table_remove(action->meta, XML_LRM_ATTR_INTERVAL);
1044 }
1045
1046 /*
1047 * Timeout order of precedence:
1048 * 1. pcmk_monitor_timeout (if rsc has pcmk_ra_cap_fence_params
1049 * and task is start or a probe; pcmk_monitor_timeout works
1050 * by default for a recurring monitor)
1051 * 2. explicit op timeout on the primitive
1052 * 3. default op timeout
1053 * a. if probe, then min-interval monitor's timeout
1054 * b. else, in XML_CIB_TAG_OPCONFIG
1055 * 4. CRM_DEFAULT_OP_TIMEOUT_S
1056 *
1057 * #1 overrides general rule of <op> XML property having highest
1058 * precedence.
1059 */
1060 if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
1061 pcmk_ra_cap_fence_params)
1062 && (pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)
1063 || is_probe)) {
1064
1065 GHashTable *params = pe_rsc_params(action->rsc, action->node, data_set);
1066
1067 value = g_hash_table_lookup(params, "pcmk_monitor_timeout");
1068
1069 if (value) {
1070 crm_trace("\t%s: Setting timeout to pcmk_monitor_timeout '%s', "
1071 "overriding default", action->uuid, value);
1072 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1073 strdup(value));
1074 }
1075 }
1076
1077 // Normalize timeout to positive milliseconds
1078 value = g_hash_table_lookup(action->meta, XML_ATTR_TIMEOUT);
1079 timeout_ms = unpack_timeout(value);
1080 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1081 pcmk__itoa(timeout_ms));
1082
1083 if (!pcmk__strcase_any_of(action->task, RSC_START, RSC_PROMOTE, NULL)) {
1084 action->needs = rsc_req_nothing;
1085 value = "nothing (not start or promote)";
1086
1087 } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_fencing)) {
1088 action->needs = rsc_req_stonith;
1089 value = "fencing";
1090
1091 } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_quorum)) {
1092 action->needs = rsc_req_quorum;
1093 value = "quorum";
1094
1095 } else {
1096 action->needs = rsc_req_nothing;
1097 value = "nothing";
1098 }
1099 pe_rsc_trace(action->rsc, "%s requires %s", action->uuid, value);
1100
1101 value = unpack_operation_on_fail(action);
1102
1103 if (value == NULL) {
1104
1105 } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) {
1106 action->on_fail = action_fail_block;
1107 g_hash_table_insert(action->meta, strdup(XML_OP_ATTR_ON_FAIL), strdup("block"));
1108 value = "block"; // The above could destroy the original string
1109
1110 } else if (pcmk__str_eq(value, "fence", pcmk__str_casei)) {
1111 action->on_fail = action_fail_fence;
1112 value = "node fencing";
1113
1114 if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1115 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for "
1116 "operation '%s' to 'stop' because 'fence' is not "
1117 "valid when fencing is disabled", action->uuid);
1118 action->on_fail = action_fail_stop;
1119 action->fail_role = RSC_ROLE_STOPPED;
1120 value = "stop resource";
1121 }
1122
1123 } else if (pcmk__str_eq(value, "standby", pcmk__str_casei)) {
1124 action->on_fail = action_fail_standby;
1125 value = "node standby";
1126
1127 } else if (pcmk__strcase_any_of(value, "ignore", "nothing", NULL)) {
1128 action->on_fail = action_fail_ignore;
1129 value = "ignore";
1130
1131 } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
1132 action->on_fail = action_fail_migrate;
1133 value = "force migration";
1134
1135 } else if (pcmk__str_eq(value, "stop", pcmk__str_casei)) {
1136 action->on_fail = action_fail_stop;
1137 action->fail_role = RSC_ROLE_STOPPED;
1138 value = "stop resource";
1139
1140 } else if (pcmk__str_eq(value, "restart", pcmk__str_casei)) {
1141 action->on_fail = action_fail_recover;
1142 value = "restart (and possibly migrate)";
1143
1144 } else if (pcmk__str_eq(value, "restart-container", pcmk__str_casei)) {
1145 if (container) {
1146 action->on_fail = action_fail_restart_container;
1147 value = "restart container (and possibly migrate)";
1148
1149 } else {
1150 value = NULL;
1151 }
1152
1153 } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
1154 action->on_fail = action_fail_demote;
1155 value = "demote instance";
1156
1157 } else {
1158 pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
1159 value = NULL;
1160 }
1161
1162 /* defaults */
1163 if (value == NULL && container) {
1164 action->on_fail = action_fail_restart_container;
1165 value = "restart container (and possibly migrate) (default)";
1166
1167 /* For remote nodes, ensure that any failure that results in dropping an
1168 * active connection to the node results in fencing of the node.
1169 *
1170 * There are only two action failures that don't result in fencing.
1171 * 1. probes - probe failures are expected.
1172 * 2. start - a start failure indicates that an active connection does not already
1173 * exist. The user can set op on-fail=fence if they really want to fence start
1174 * failures. */
1175 } else if (((value == NULL) || !pcmk_is_set(action->rsc->flags, pe_rsc_managed))
1176 && pe__resource_is_remote_conn(action->rsc, data_set)
1177 && !(pcmk__str_eq(action->task, CRMD_ACTION_STATUS, pcmk__str_casei)
1178 && (interval_ms == 0))
1179 && !pcmk__str_eq(action->task, CRMD_ACTION_START, pcmk__str_casei)) {
1180
1181 if (!pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
1182 action->on_fail = action_fail_stop;
1183 action->fail_role = RSC_ROLE_STOPPED;
1184 value = "stop unmanaged remote node (enforcing default)";
1185
1186 } else {
1187 if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1188 value = "fence remote node (default)";
1189 } else {
1190 value = "recover remote node connection (default)";
1191 }
1192
1193 if (action->rsc->remote_reconnect_ms) {
1194 action->fail_role = RSC_ROLE_STOPPED;
1195 }
1196 action->on_fail = action_fail_reset_remote;
1197 }
1198
1199 } else if (value == NULL && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
1200 if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1201 action->on_fail = action_fail_fence;
1202 value = "resource fence (default)";
1203
1204 } else {
1205 action->on_fail = action_fail_block;
1206 value = "resource block (default)";
1207 }
1208
1209 } else if (value == NULL) {
1210 action->on_fail = action_fail_recover;
1211 value = "restart (and possibly migrate) (default)";
1212 }
1213
1214 pe_rsc_trace(action->rsc, "%s failure handling: %s",
1215 action->uuid, value);
1216
1217 value = NULL;
1218 if (xml_obj != NULL) {
1219 value = g_hash_table_lookup(action->meta, "role_after_failure");
1220 if (value) {
1221 pe_warn_once(pe_wo_role_after,
1222 "Support for role_after_failure is deprecated and will be removed in a future release");
1223 }
1224 }
1225 if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
1226 action->fail_role = text2role(value);
1227 }
1228 /* defaults */
1229 if (action->fail_role == RSC_ROLE_UNKNOWN) {
1230 if (pcmk__str_eq(action->task, CRMD_ACTION_PROMOTE, pcmk__str_casei)) {
1231 action->fail_role = RSC_ROLE_UNPROMOTED;
1232 } else {
1233 action->fail_role = RSC_ROLE_STARTED;
1234 }
1235 }
1236 pe_rsc_trace(action->rsc, "%s failure results in: %s",
1237 action->uuid, role2text(action->fail_role));
1238
1239 value = g_hash_table_lookup(action->meta, XML_OP_ATTR_START_DELAY);
1240 if (value) {
1241 unpack_start_delay(value, action->meta);
1242 } else {
1243 long long start_delay = 0;
1244
1245 value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
1246 if (unpack_interval_origin(value, xml_obj, interval_ms, data_set->now,
1247 &start_delay)) {
1248 g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY),
1249 crm_strdup_printf("%lld", start_delay));
1250 }
1251 }
1252
1253 #if ENABLE_VERSIONED_ATTRS
1254 unpack_versioned_meta(rsc_details->versioned_meta, xml_obj, interval_ms,
1255 data_set->now);
1256 #endif
1257 }
1258
1259 static xmlNode *
find_rsc_op_entry_helper(pe_resource_t * rsc,const char * key,gboolean include_disabled)1260 find_rsc_op_entry_helper(pe_resource_t * rsc, const char *key, gboolean include_disabled)
1261 {
1262 guint interval_ms = 0;
1263 gboolean do_retry = TRUE;
1264 char *local_key = NULL;
1265 const char *name = NULL;
1266 const char *value = NULL;
1267 const char *interval_spec = NULL;
1268 char *match_key = NULL;
1269 xmlNode *op = NULL;
1270 xmlNode *operation = NULL;
1271
1272 retry:
1273 for (operation = pcmk__xe_first_child(rsc->ops_xml); operation != NULL;
1274 operation = pcmk__xe_next(operation)) {
1275
1276 if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
1277 name = crm_element_value(operation, "name");
1278 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
1279 value = crm_element_value(operation, "enabled");
1280 if (!include_disabled && value && crm_is_true(value) == FALSE) {
1281 continue;
1282 }
1283
1284 interval_ms = crm_parse_interval_spec(interval_spec);
1285 match_key = pcmk__op_key(rsc->id, name, interval_ms);
1286 if (pcmk__str_eq(key, match_key, pcmk__str_casei)) {
1287 op = operation;
1288 }
1289 free(match_key);
1290
1291 if (rsc->clone_name) {
1292 match_key = pcmk__op_key(rsc->clone_name, name, interval_ms);
1293 if (pcmk__str_eq(key, match_key, pcmk__str_casei)) {
1294 op = operation;
1295 }
1296 free(match_key);
1297 }
1298
1299 if (op != NULL) {
1300 free(local_key);
1301 return op;
1302 }
1303 }
1304 }
1305
1306 free(local_key);
1307 if (do_retry == FALSE) {
1308 return NULL;
1309 }
1310
1311 do_retry = FALSE;
1312 if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
1313 local_key = pcmk__op_key(rsc->id, "migrate", 0);
1314 key = local_key;
1315 goto retry;
1316
1317 } else if (strstr(key, "_notify_")) {
1318 local_key = pcmk__op_key(rsc->id, "notify", 0);
1319 key = local_key;
1320 goto retry;
1321 }
1322
1323 return NULL;
1324 }
1325
1326 xmlNode *
find_rsc_op_entry(pe_resource_t * rsc,const char * key)1327 find_rsc_op_entry(pe_resource_t * rsc, const char *key)
1328 {
1329 return find_rsc_op_entry_helper(rsc, key, FALSE);
1330 }
1331
1332 /*
1333 * Used by the HashTable for-loop
1334 */
1335 void
print_str_str(gpointer key,gpointer value,gpointer user_data)1336 print_str_str(gpointer key, gpointer value, gpointer user_data)
1337 {
1338 crm_trace("%s%s %s ==> %s",
1339 user_data == NULL ? "" : (char *)user_data,
1340 user_data == NULL ? "" : ": ", (char *)key, (char *)value);
1341 }
1342
1343 void
pe_free_action(pe_action_t * action)1344 pe_free_action(pe_action_t * action)
1345 {
1346 if (action == NULL) {
1347 return;
1348 }
1349 g_list_free_full(action->actions_before, free); /* pe_action_wrapper_t* */
1350 g_list_free_full(action->actions_after, free); /* pe_action_wrapper_t* */
1351 if (action->extra) {
1352 g_hash_table_destroy(action->extra);
1353 }
1354 if (action->meta) {
1355 g_hash_table_destroy(action->meta);
1356 }
1357 #if ENABLE_VERSIONED_ATTRS
1358 if (action->rsc) {
1359 pe_free_rsc_action_details(action);
1360 }
1361 #endif
1362 free(action->cancel_task);
1363 free(action->reason);
1364 free(action->task);
1365 free(action->uuid);
1366 free(action->node);
1367 free(action);
1368 }
1369
1370 GList *
find_recurring_actions(GList * input,pe_node_t * not_on_node)1371 find_recurring_actions(GList *input, pe_node_t * not_on_node)
1372 {
1373 const char *value = NULL;
1374 GList *result = NULL;
1375 GList *gIter = input;
1376
1377 CRM_CHECK(input != NULL, return NULL);
1378
1379 for (; gIter != NULL; gIter = gIter->next) {
1380 pe_action_t *action = (pe_action_t *) gIter->data;
1381
1382 value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL_MS);
1383 if (value == NULL) {
1384 /* skip */
1385 } else if (pcmk__str_eq(value, "0", pcmk__str_casei)) {
1386 /* skip */
1387 } else if (pcmk__str_eq(CRMD_ACTION_CANCEL, action->task, pcmk__str_casei)) {
1388 /* skip */
1389 } else if (not_on_node == NULL) {
1390 crm_trace("(null) Found: %s", action->uuid);
1391 result = g_list_prepend(result, action);
1392
1393 } else if (action->node == NULL) {
1394 /* skip */
1395 } else if (action->node->details != not_on_node->details) {
1396 crm_trace("Found: %s", action->uuid);
1397 result = g_list_prepend(result, action);
1398 }
1399 }
1400
1401 return result;
1402 }
1403
1404 enum action_tasks
get_complex_task(pe_resource_t * rsc,const char * name,gboolean allow_non_atomic)1405 get_complex_task(pe_resource_t * rsc, const char *name, gboolean allow_non_atomic)
1406 {
1407 enum action_tasks task = text2task(name);
1408
1409 if (rsc == NULL) {
1410 return task;
1411
1412 } else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
1413 switch (task) {
1414 case stopped_rsc:
1415 case started_rsc:
1416 case action_demoted:
1417 case action_promoted:
1418 crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
1419 return task - 1;
1420 default:
1421 break;
1422 }
1423 }
1424 return task;
1425 }
1426
1427 pe_action_t *
find_first_action(GList * input,const char * uuid,const char * task,pe_node_t * on_node)1428 find_first_action(GList *input, const char *uuid, const char *task, pe_node_t * on_node)
1429 {
1430 GList *gIter = NULL;
1431
1432 CRM_CHECK(uuid || task, return NULL);
1433
1434 for (gIter = input; gIter != NULL; gIter = gIter->next) {
1435 pe_action_t *action = (pe_action_t *) gIter->data;
1436
1437 if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1438 continue;
1439
1440 } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1441 continue;
1442
1443 } else if (on_node == NULL) {
1444 return action;
1445
1446 } else if (action->node == NULL) {
1447 continue;
1448
1449 } else if (on_node->details == action->node->details) {
1450 return action;
1451 }
1452 }
1453
1454 return NULL;
1455 }
1456
1457 GList *
find_actions(GList * input,const char * key,const pe_node_t * on_node)1458 find_actions(GList *input, const char *key, const pe_node_t *on_node)
1459 {
1460 GList *gIter = input;
1461 GList *result = NULL;
1462
1463 CRM_CHECK(key != NULL, return NULL);
1464
1465 for (; gIter != NULL; gIter = gIter->next) {
1466 pe_action_t *action = (pe_action_t *) gIter->data;
1467
1468 if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1469 crm_trace("%s does not match action %s", key, action->uuid);
1470 continue;
1471
1472 } else if (on_node == NULL) {
1473 crm_trace("Action %s matches (ignoring node)", key);
1474 result = g_list_prepend(result, action);
1475
1476 } else if (action->node == NULL) {
1477 crm_trace("Action %s matches (unallocated, assigning to %s)",
1478 key, on_node->details->uname);
1479
1480 action->node = pe__copy_node(on_node);
1481 result = g_list_prepend(result, action);
1482
1483 } else if (on_node->details == action->node->details) {
1484 crm_trace("Action %s on %s matches", key, on_node->details->uname);
1485 result = g_list_prepend(result, action);
1486
1487 } else {
1488 crm_trace("Action %s on node %s does not match requested node %s",
1489 key, action->node->details->uname,
1490 on_node->details->uname);
1491 }
1492 }
1493
1494 return result;
1495 }
1496
1497 GList *
find_actions_exact(GList * input,const char * key,const pe_node_t * on_node)1498 find_actions_exact(GList *input, const char *key, const pe_node_t *on_node)
1499 {
1500 GList *result = NULL;
1501
1502 CRM_CHECK(key != NULL, return NULL);
1503
1504 if (on_node == NULL) {
1505 crm_trace("Not searching for action %s because node not specified",
1506 key);
1507 return NULL;
1508 }
1509
1510 for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1511 pe_action_t *action = (pe_action_t *) gIter->data;
1512
1513 if (action->node == NULL) {
1514 crm_trace("Skipping comparison of %s vs action %s without node",
1515 key, action->uuid);
1516
1517 } else if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1518 crm_trace("Desired action %s doesn't match %s", key, action->uuid);
1519
1520 } else if (!pcmk__str_eq(on_node->details->id, action->node->details->id, pcmk__str_casei)) {
1521 crm_trace("Action %s desired node ID %s doesn't match %s",
1522 key, on_node->details->id, action->node->details->id);
1523
1524 } else {
1525 crm_trace("Action %s matches", key);
1526 result = g_list_prepend(result, action);
1527 }
1528 }
1529
1530 return result;
1531 }
1532
1533 /*!
1534 * \brief Find all actions of given type for a resource
1535 *
1536 * \param[in] rsc Resource to search
1537 * \param[in] node Find only actions scheduled on this node
1538 * \param[in] task Action name to search for
1539 * \param[in] require_node If TRUE, NULL node or action node will not match
1540 *
1541 * \return List of actions found (or NULL if none)
1542 * \note If node is not NULL and require_node is FALSE, matching actions
1543 * without a node will be assigned to node.
1544 */
1545 GList *
pe__resource_actions(const pe_resource_t * rsc,const pe_node_t * node,const char * task,bool require_node)1546 pe__resource_actions(const pe_resource_t *rsc, const pe_node_t *node,
1547 const char *task, bool require_node)
1548 {
1549 GList *result = NULL;
1550 char *key = pcmk__op_key(rsc->id, task, 0);
1551
1552 if (require_node) {
1553 result = find_actions_exact(rsc->actions, key, node);
1554 } else {
1555 result = find_actions(rsc->actions, key, node);
1556 }
1557 free(key);
1558 return result;
1559 }
1560
1561 static void
resource_node_score(pe_resource_t * rsc,pe_node_t * node,int score,const char * tag)1562 resource_node_score(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag)
1563 {
1564 pe_node_t *match = NULL;
1565
1566 if ((rsc->exclusive_discover || (node->rsc_discover_mode == pe_discover_never))
1567 && pcmk__str_eq(tag, "symmetric_default", pcmk__str_casei)) {
1568 /* This string comparision may be fragile, but exclusive resources and
1569 * exclusive nodes should not have the symmetric_default constraint
1570 * applied to them.
1571 */
1572 return;
1573
1574 } else if (rsc->children) {
1575 GList *gIter = rsc->children;
1576
1577 for (; gIter != NULL; gIter = gIter->next) {
1578 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1579
1580 resource_node_score(child_rsc, node, score, tag);
1581 }
1582 }
1583
1584 pe_rsc_trace(rsc, "Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score);
1585 match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
1586 if (match == NULL) {
1587 match = pe__copy_node(node);
1588 g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
1589 }
1590 match->weight = pe__add_scores(match->weight, score);
1591 }
1592
1593 void
resource_location(pe_resource_t * rsc,pe_node_t * node,int score,const char * tag,pe_working_set_t * data_set)1594 resource_location(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag,
1595 pe_working_set_t * data_set)
1596 {
1597 if (node != NULL) {
1598 resource_node_score(rsc, node, score, tag);
1599
1600 } else if (data_set != NULL) {
1601 GList *gIter = data_set->nodes;
1602
1603 for (; gIter != NULL; gIter = gIter->next) {
1604 pe_node_t *node_iter = (pe_node_t *) gIter->data;
1605
1606 resource_node_score(rsc, node_iter, score, tag);
1607 }
1608
1609 } else {
1610 GHashTableIter iter;
1611 pe_node_t *node_iter = NULL;
1612
1613 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1614 while (g_hash_table_iter_next(&iter, NULL, (void **)&node_iter)) {
1615 resource_node_score(rsc, node_iter, score, tag);
1616 }
1617 }
1618
1619 if (node == NULL && score == -INFINITY) {
1620 if (rsc->allocated_to) {
1621 crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
1622 free(rsc->allocated_to);
1623 rsc->allocated_to = NULL;
1624 }
1625 }
1626 }
1627
1628 #define sort_return(an_int, why) do { \
1629 free(a_uuid); \
1630 free(b_uuid); \
1631 crm_trace("%s (%d) %c %s (%d) : %s", \
1632 a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
1633 b_xml_id, b_call_id, why); \
1634 return an_int; \
1635 } while(0)
1636
1637 gint
sort_op_by_callid(gconstpointer a,gconstpointer b)1638 sort_op_by_callid(gconstpointer a, gconstpointer b)
1639 {
1640 int a_call_id = -1;
1641 int b_call_id = -1;
1642
1643 char *a_uuid = NULL;
1644 char *b_uuid = NULL;
1645
1646 const xmlNode *xml_a = a;
1647 const xmlNode *xml_b = b;
1648
1649 const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID);
1650 const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID);
1651
1652 if (pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_casei)) {
1653 /* We have duplicate lrm_rsc_op entries in the status
1654 * section which is unlikely to be a good thing
1655 * - we can handle it easily enough, but we need to get
1656 * to the bottom of why it's happening.
1657 */
1658 pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
1659 sort_return(0, "duplicate");
1660 }
1661
1662 crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
1663 crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
1664
1665 if (a_call_id == -1 && b_call_id == -1) {
1666 /* both are pending ops so it doesn't matter since
1667 * stops are never pending
1668 */
1669 sort_return(0, "pending");
1670
1671 } else if (a_call_id >= 0 && a_call_id < b_call_id) {
1672 sort_return(-1, "call id");
1673
1674 } else if (b_call_id >= 0 && a_call_id > b_call_id) {
1675 sort_return(1, "call id");
1676
1677 } else if (b_call_id >= 0 && a_call_id == b_call_id) {
1678 /*
1679 * The op and last_failed_op are the same
1680 * Order on last-rc-change
1681 */
1682 time_t last_a = -1;
1683 time_t last_b = -1;
1684
1685 crm_element_value_epoch(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
1686 crm_element_value_epoch(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
1687
1688 crm_trace("rc-change: %lld vs %lld",
1689 (long long) last_a, (long long) last_b);
1690 if (last_a >= 0 && last_a < last_b) {
1691 sort_return(-1, "rc-change");
1692
1693 } else if (last_b >= 0 && last_a > last_b) {
1694 sort_return(1, "rc-change");
1695 }
1696 sort_return(0, "rc-change");
1697
1698 } else {
1699 /* One of the inputs is a pending operation
1700 * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
1701 */
1702
1703 int a_id = -1;
1704 int b_id = -1;
1705
1706 const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC);
1707 const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC);
1708
1709 CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1710 if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1711 NULL)) {
1712 sort_return(0, "bad magic a");
1713 }
1714 if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1715 NULL)) {
1716 sort_return(0, "bad magic b");
1717 }
1718 /* try to determine the relative age of the operation...
1719 * some pending operations (e.g. a start) may have been superseded
1720 * by a subsequent stop
1721 *
1722 * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1723 */
1724 if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1725 /*
1726 * some of the logic in here may be redundant...
1727 *
1728 * if the UUID from the TE doesn't match then one better
1729 * be a pending operation.
1730 * pending operations don't survive between elections and joins
1731 * because we query the LRM directly
1732 */
1733
1734 if (b_call_id == -1) {
1735 sort_return(-1, "transition + call");
1736
1737 } else if (a_call_id == -1) {
1738 sort_return(1, "transition + call");
1739 }
1740
1741 } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1742 sort_return(-1, "transition");
1743
1744 } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1745 sort_return(1, "transition");
1746 }
1747 }
1748
1749 /* we should never end up here */
1750 CRM_CHECK(FALSE, sort_return(0, "default"));
1751
1752 }
1753
1754 time_t
get_effective_time(pe_working_set_t * data_set)1755 get_effective_time(pe_working_set_t * data_set)
1756 {
1757 if(data_set) {
1758 if (data_set->now == NULL) {
1759 crm_trace("Recording a new 'now'");
1760 data_set->now = crm_time_new(NULL);
1761 }
1762 return crm_time_get_seconds_since_epoch(data_set->now);
1763 }
1764
1765 crm_trace("Defaulting to 'now'");
1766 return time(NULL);
1767 }
1768
1769 gboolean
get_target_role(pe_resource_t * rsc,enum rsc_role_e * role)1770 get_target_role(pe_resource_t * rsc, enum rsc_role_e * role)
1771 {
1772 enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
1773 const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
1774
1775 CRM_CHECK(role != NULL, return FALSE);
1776
1777 if (pcmk__str_eq(value, "started", pcmk__str_null_matches | pcmk__str_casei)
1778 || pcmk__str_eq("default", value, pcmk__str_casei)) {
1779 return FALSE;
1780 }
1781
1782 local_role = text2role(value);
1783 if (local_role == RSC_ROLE_UNKNOWN) {
1784 pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s "
1785 "because '%s' is not valid", rsc->id, value);
1786 return FALSE;
1787
1788 } else if (local_role > RSC_ROLE_STARTED) {
1789 if (pcmk_is_set(uber_parent(rsc)->flags, pe_rsc_promotable)) {
1790 if (local_role > RSC_ROLE_UNPROMOTED) {
1791 /* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
1792 return FALSE;
1793 }
1794
1795 } else {
1796 pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s "
1797 "because '%s' only makes sense for promotable "
1798 "clones", rsc->id, value);
1799 return FALSE;
1800 }
1801 }
1802
1803 *role = local_role;
1804 return TRUE;
1805 }
1806
1807 gboolean
order_actions(pe_action_t * lh_action,pe_action_t * rh_action,enum pe_ordering order)1808 order_actions(pe_action_t * lh_action, pe_action_t * rh_action, enum pe_ordering order)
1809 {
1810 GList *gIter = NULL;
1811 pe_action_wrapper_t *wrapper = NULL;
1812 GList *list = NULL;
1813
1814 if (order == pe_order_none) {
1815 return FALSE;
1816 }
1817
1818 if (lh_action == NULL || rh_action == NULL) {
1819 return FALSE;
1820 }
1821
1822 crm_trace("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid);
1823
1824 /* Ensure we never create a dependency on ourselves... it's happened */
1825 CRM_ASSERT(lh_action != rh_action);
1826
1827 /* Filter dups, otherwise update_action_states() has too much work to do */
1828 gIter = lh_action->actions_after;
1829 for (; gIter != NULL; gIter = gIter->next) {
1830 pe_action_wrapper_t *after = (pe_action_wrapper_t *) gIter->data;
1831
1832 if (after->action == rh_action && (after->type & order)) {
1833 return FALSE;
1834 }
1835 }
1836
1837 wrapper = calloc(1, sizeof(pe_action_wrapper_t));
1838 wrapper->action = rh_action;
1839 wrapper->type = order;
1840 list = lh_action->actions_after;
1841 list = g_list_prepend(list, wrapper);
1842 lh_action->actions_after = list;
1843
1844 wrapper = calloc(1, sizeof(pe_action_wrapper_t));
1845 wrapper->action = lh_action;
1846 wrapper->type = order;
1847 list = rh_action->actions_before;
1848 list = g_list_prepend(list, wrapper);
1849 rh_action->actions_before = list;
1850 return TRUE;
1851 }
1852
1853 pe_action_t *
get_pseudo_op(const char * name,pe_working_set_t * data_set)1854 get_pseudo_op(const char *name, pe_working_set_t * data_set)
1855 {
1856 pe_action_t *op = NULL;
1857
1858 if(data_set->singletons) {
1859 op = g_hash_table_lookup(data_set->singletons, name);
1860 }
1861 if (op == NULL) {
1862 op = custom_action(NULL, strdup(name), name, NULL, TRUE, TRUE, data_set);
1863 pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
1864 }
1865
1866 return op;
1867 }
1868
1869 void
destroy_ticket(gpointer data)1870 destroy_ticket(gpointer data)
1871 {
1872 pe_ticket_t *ticket = data;
1873
1874 if (ticket->state) {
1875 g_hash_table_destroy(ticket->state);
1876 }
1877 free(ticket->id);
1878 free(ticket);
1879 }
1880
1881 pe_ticket_t *
ticket_new(const char * ticket_id,pe_working_set_t * data_set)1882 ticket_new(const char *ticket_id, pe_working_set_t * data_set)
1883 {
1884 pe_ticket_t *ticket = NULL;
1885
1886 if (pcmk__str_empty(ticket_id)) {
1887 return NULL;
1888 }
1889
1890 if (data_set->tickets == NULL) {
1891 data_set->tickets = pcmk__strkey_table(free, destroy_ticket);
1892 }
1893
1894 ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
1895 if (ticket == NULL) {
1896
1897 ticket = calloc(1, sizeof(pe_ticket_t));
1898 if (ticket == NULL) {
1899 crm_err("Cannot allocate ticket '%s'", ticket_id);
1900 return NULL;
1901 }
1902
1903 crm_trace("Creaing ticket entry for %s", ticket_id);
1904
1905 ticket->id = strdup(ticket_id);
1906 ticket->granted = FALSE;
1907 ticket->last_granted = -1;
1908 ticket->standby = FALSE;
1909 ticket->state = pcmk__strkey_table(free, free);
1910
1911 g_hash_table_insert(data_set->tickets, strdup(ticket->id), ticket);
1912 }
1913
1914 return ticket;
1915 }
1916
rsc_printable_id(pe_resource_t * rsc)1917 const char *rsc_printable_id(pe_resource_t *rsc)
1918 {
1919 if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
1920 return ID(rsc->xml);
1921 }
1922 return rsc->id;
1923 }
1924
1925 void
pe__clear_resource_flags_recursive(pe_resource_t * rsc,uint64_t flags)1926 pe__clear_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
1927 {
1928 pe__clear_resource_flags(rsc, flags);
1929 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1930 pe__clear_resource_flags_recursive((pe_resource_t *) gIter->data, flags);
1931 }
1932 }
1933
1934 void
pe__clear_resource_flags_on_all(pe_working_set_t * data_set,uint64_t flag)1935 pe__clear_resource_flags_on_all(pe_working_set_t *data_set, uint64_t flag)
1936 {
1937 for (GList *lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
1938 pe_resource_t *r = (pe_resource_t *) lpc->data;
1939 pe__clear_resource_flags_recursive(r, flag);
1940 }
1941 }
1942
1943 void
pe__set_resource_flags_recursive(pe_resource_t * rsc,uint64_t flags)1944 pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
1945 {
1946 pe__set_resource_flags(rsc, flags);
1947 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1948 pe__set_resource_flags_recursive((pe_resource_t *) gIter->data, flags);
1949 }
1950 }
1951
1952 static GList *
find_unfencing_devices(GList * candidates,GList * matches)1953 find_unfencing_devices(GList *candidates, GList *matches)
1954 {
1955 for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
1956 pe_resource_t *candidate = gIter->data;
1957 const char *provides = g_hash_table_lookup(candidate->meta,
1958 PCMK_STONITH_PROVIDES);
1959 const char *requires = g_hash_table_lookup(candidate->meta, XML_RSC_ATTR_REQUIRES);
1960
1961 if(candidate->children) {
1962 matches = find_unfencing_devices(candidate->children, matches);
1963 } else if (!pcmk_is_set(candidate->flags, pe_rsc_fence_device)) {
1964 continue;
1965
1966 } else if (pcmk__str_eq(provides, "unfencing", pcmk__str_casei) || pcmk__str_eq(requires, "unfencing", pcmk__str_casei)) {
1967 matches = g_list_prepend(matches, candidate);
1968 }
1969 }
1970 return matches;
1971 }
1972
1973 static int
node_priority_fencing_delay(pe_node_t * node,pe_working_set_t * data_set)1974 node_priority_fencing_delay(pe_node_t * node, pe_working_set_t * data_set)
1975 {
1976 int member_count = 0;
1977 int online_count = 0;
1978 int top_priority = 0;
1979 int lowest_priority = 0;
1980 GList *gIter = NULL;
1981
1982 // `priority-fencing-delay` is disabled
1983 if (data_set->priority_fencing_delay <= 0) {
1984 return 0;
1985 }
1986
1987 /* No need to request a delay if the fencing target is not a normal cluster
1988 * member, for example if it's a remote node or a guest node. */
1989 if (node->details->type != node_member) {
1990 return 0;
1991 }
1992
1993 // No need to request a delay if the fencing target is in our partition
1994 if (node->details->online) {
1995 return 0;
1996 }
1997
1998 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
1999 pe_node_t *n = gIter->data;
2000
2001 if (n->details->type != node_member) {
2002 continue;
2003 }
2004
2005 member_count ++;
2006
2007 if (n->details->online) {
2008 online_count++;
2009 }
2010
2011 if (member_count == 1
2012 || n->details->priority > top_priority) {
2013 top_priority = n->details->priority;
2014 }
2015
2016 if (member_count == 1
2017 || n->details->priority < lowest_priority) {
2018 lowest_priority = n->details->priority;
2019 }
2020 }
2021
2022 // No need to delay if we have more than half of the cluster members
2023 if (online_count > member_count / 2) {
2024 return 0;
2025 }
2026
2027 /* All the nodes have equal priority.
2028 * Any configured corresponding `pcmk_delay_base/max` will be applied. */
2029 if (lowest_priority == top_priority) {
2030 return 0;
2031 }
2032
2033 if (node->details->priority < top_priority) {
2034 return 0;
2035 }
2036
2037 return data_set->priority_fencing_delay;
2038 }
2039
2040 pe_action_t *
pe_fence_op(pe_node_t * node,const char * op,bool optional,const char * reason,bool priority_delay,pe_working_set_t * data_set)2041 pe_fence_op(pe_node_t * node, const char *op, bool optional, const char *reason,
2042 bool priority_delay, pe_working_set_t * data_set)
2043 {
2044 char *op_key = NULL;
2045 pe_action_t *stonith_op = NULL;
2046
2047 if(op == NULL) {
2048 op = data_set->stonith_action;
2049 }
2050
2051 op_key = crm_strdup_printf("%s-%s-%s", CRM_OP_FENCE, node->details->uname, op);
2052
2053 if(data_set->singletons) {
2054 stonith_op = g_hash_table_lookup(data_set->singletons, op_key);
2055 }
2056
2057 if(stonith_op == NULL) {
2058 stonith_op = custom_action(NULL, op_key, CRM_OP_FENCE, node, TRUE, TRUE, data_set);
2059
2060 add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
2061 add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
2062 add_hash_param(stonith_op->meta, "stonith_action", op);
2063
2064 if (pe__is_guest_or_remote_node(node)
2065 && pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
2066 /* Extra work to detect device changes on remotes
2067 *
2068 * We may do this for all nodes in the future, but for now
2069 * the check_action_definition() based stuff works fine.
2070 */
2071 long max = 1024;
2072 long digests_all_offset = 0;
2073 long digests_secure_offset = 0;
2074
2075 char *digests_all = calloc(max, sizeof(char));
2076 char *digests_secure = calloc(max, sizeof(char));
2077 GList *matches = find_unfencing_devices(data_set->resources, NULL);
2078
2079 for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
2080 pe_resource_t *match = gIter->data;
2081 const char *agent = g_hash_table_lookup(match->meta,
2082 XML_ATTR_TYPE);
2083 op_digest_cache_t *data = NULL;
2084
2085 data = pe__compare_fencing_digest(match, agent, node, data_set);
2086 if(data->rc == RSC_DIGEST_ALL) {
2087 optional = FALSE;
2088 crm_notice("Unfencing %s (remote): because the definition of %s changed", node->details->uname, match->id);
2089 if (!pcmk__is_daemon && data_set->priv != NULL) {
2090 pcmk__output_t *out = data_set->priv;
2091 out->info(out, "notice: Unfencing %s (remote): because the definition of %s changed",
2092 node->details->uname, match->id);
2093 }
2094 }
2095
2096 digests_all_offset += snprintf(
2097 digests_all+digests_all_offset, max-digests_all_offset,
2098 "%s:%s:%s,", match->id, agent, data->digest_all_calc);
2099
2100 digests_secure_offset += snprintf(
2101 digests_secure+digests_secure_offset, max-digests_secure_offset,
2102 "%s:%s:%s,", match->id, agent, data->digest_secure_calc);
2103 }
2104 g_hash_table_insert(stonith_op->meta,
2105 strdup(XML_OP_ATTR_DIGESTS_ALL),
2106 digests_all);
2107 g_hash_table_insert(stonith_op->meta,
2108 strdup(XML_OP_ATTR_DIGESTS_SECURE),
2109 digests_secure);
2110 }
2111
2112 } else {
2113 free(op_key);
2114 }
2115
2116 if (data_set->priority_fencing_delay > 0
2117
2118 /* It's a suitable case where `priority-fencing-delay` applies.
2119 * At least add `priority-fencing-delay` field as an indicator. */
2120 && (priority_delay
2121
2122 /* Re-calculate priority delay for the suitable case when
2123 * pe_fence_op() is called again by stage6() after node priority has
2124 * been actually calculated with native_add_running() */
2125 || g_hash_table_lookup(stonith_op->meta,
2126 XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY) != NULL)) {
2127
2128 /* Add `priority-fencing-delay` to the fencing op even if it's 0 for
2129 * the targeting node. So that it takes precedence over any possible
2130 * `pcmk_delay_base/max`.
2131 */
2132 char *delay_s = pcmk__itoa(node_priority_fencing_delay(node, data_set));
2133
2134 g_hash_table_insert(stonith_op->meta,
2135 strdup(XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY),
2136 delay_s);
2137 }
2138
2139 if(optional == FALSE && pe_can_fence(data_set, node)) {
2140 pe_action_required(stonith_op, NULL, reason);
2141 } else if(reason && stonith_op->reason == NULL) {
2142 stonith_op->reason = strdup(reason);
2143 }
2144
2145 return stonith_op;
2146 }
2147
2148 void
trigger_unfencing(pe_resource_t * rsc,pe_node_t * node,const char * reason,pe_action_t * dependency,pe_working_set_t * data_set)2149 trigger_unfencing(
2150 pe_resource_t * rsc, pe_node_t *node, const char *reason, pe_action_t *dependency, pe_working_set_t * data_set)
2151 {
2152 if (!pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
2153 /* No resources require it */
2154 return;
2155
2156 } else if ((rsc != NULL)
2157 && !pcmk_is_set(rsc->flags, pe_rsc_fence_device)) {
2158 /* Wasn't a stonith device */
2159 return;
2160
2161 } else if(node
2162 && node->details->online
2163 && node->details->unclean == FALSE
2164 && node->details->shutdown == FALSE) {
2165 pe_action_t *unfence = pe_fence_op(node, "on", FALSE, reason, FALSE, data_set);
2166
2167 if(dependency) {
2168 order_actions(unfence, dependency, pe_order_optional);
2169 }
2170
2171 } else if(rsc) {
2172 GHashTableIter iter;
2173
2174 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
2175 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
2176 if(node->details->online && node->details->unclean == FALSE && node->details->shutdown == FALSE) {
2177 trigger_unfencing(rsc, node, reason, dependency, data_set);
2178 }
2179 }
2180 }
2181 }
2182
2183 gboolean
add_tag_ref(GHashTable * tags,const char * tag_name,const char * obj_ref)2184 add_tag_ref(GHashTable * tags, const char * tag_name, const char * obj_ref)
2185 {
2186 pe_tag_t *tag = NULL;
2187 GList *gIter = NULL;
2188 gboolean is_existing = FALSE;
2189
2190 CRM_CHECK(tags && tag_name && obj_ref, return FALSE);
2191
2192 tag = g_hash_table_lookup(tags, tag_name);
2193 if (tag == NULL) {
2194 tag = calloc(1, sizeof(pe_tag_t));
2195 if (tag == NULL) {
2196 return FALSE;
2197 }
2198 tag->id = strdup(tag_name);
2199 tag->refs = NULL;
2200 g_hash_table_insert(tags, strdup(tag_name), tag);
2201 }
2202
2203 for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
2204 const char *existing_ref = (const char *) gIter->data;
2205
2206 if (pcmk__str_eq(existing_ref, obj_ref, pcmk__str_none)){
2207 is_existing = TRUE;
2208 break;
2209 }
2210 }
2211
2212 if (is_existing == FALSE) {
2213 tag->refs = g_list_append(tag->refs, strdup(obj_ref));
2214 crm_trace("Added: tag=%s ref=%s", tag->id, obj_ref);
2215 }
2216
2217 return TRUE;
2218 }
2219
pe_action_set_flag_reason(const char * function,long line,pe_action_t * action,pe_action_t * reason,const char * text,enum pe_action_flags flags,bool overwrite)2220 void pe_action_set_flag_reason(const char *function, long line,
2221 pe_action_t *action, pe_action_t *reason, const char *text,
2222 enum pe_action_flags flags, bool overwrite)
2223 {
2224 bool unset = FALSE;
2225 bool update = FALSE;
2226 const char *change = NULL;
2227
2228 if (pcmk_is_set(flags, pe_action_runnable)) {
2229 unset = TRUE;
2230 change = "unrunnable";
2231 } else if (pcmk_is_set(flags, pe_action_optional)) {
2232 unset = TRUE;
2233 change = "required";
2234 } else if (pcmk_is_set(flags, pe_action_migrate_runnable)) {
2235 unset = TRUE;
2236 overwrite = TRUE;
2237 change = "unrunnable";
2238 } else if (pcmk_is_set(flags, pe_action_dangle)) {
2239 change = "dangling";
2240 } else if (pcmk_is_set(flags, pe_action_requires_any)) {
2241 change = "required";
2242 } else {
2243 crm_err("Unknown flag change to %x by %s: 0x%s",
2244 flags, action->uuid, (reason? reason->uuid : "0"));
2245 }
2246
2247 if(unset) {
2248 if (pcmk_is_set(action->flags, flags)) {
2249 pe__clear_action_flags_as(function, line, action, flags);
2250 update = TRUE;
2251 }
2252
2253 } else {
2254 if (!pcmk_is_set(action->flags, flags)) {
2255 pe__set_action_flags_as(function, line, action, flags);
2256 update = TRUE;
2257 }
2258 }
2259
2260 if((change && update) || text) {
2261 char *reason_text = NULL;
2262 if(reason == NULL) {
2263 pe_action_set_reason(action, text, overwrite);
2264
2265 } else if(reason->rsc == NULL) {
2266 reason_text = crm_strdup_printf("%s %s%c %s", change, reason->task, text?':':0, text?text:"");
2267 } else {
2268 reason_text = crm_strdup_printf("%s %s %s%c %s", change, reason->rsc->id, reason->task, text?':':0, text?text:"NA");
2269 }
2270
2271 if(reason_text && action->rsc != reason->rsc) {
2272 pe_action_set_reason(action, reason_text, overwrite);
2273 }
2274 free(reason_text);
2275 }
2276 }
2277
pe_action_set_reason(pe_action_t * action,const char * reason,bool overwrite)2278 void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite)
2279 {
2280 if (action->reason != NULL && overwrite) {
2281 pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
2282 action->uuid, action->reason, crm_str(reason));
2283 free(action->reason);
2284 } else if (action->reason == NULL) {
2285 pe_rsc_trace(action->rsc, "Set %s reason to '%s'",
2286 action->uuid, crm_str(reason));
2287 } else {
2288 // crm_assert(action->reason != NULL && !overwrite);
2289 return;
2290 }
2291
2292 if (reason != NULL) {
2293 action->reason = strdup(reason);
2294 } else {
2295 action->reason = NULL;
2296 }
2297 }
2298
2299 /*!
2300 * \internal
2301 * \brief Check whether shutdown has been requested for a node
2302 *
2303 * \param[in] node Node to check
2304 *
2305 * \return TRUE if node has shutdown attribute set and nonzero, FALSE otherwise
2306 * \note This differs from simply using node->details->shutdown in that it can
2307 * be used before that has been determined (and in fact to determine it),
2308 * and it can also be used to distinguish requested shutdown from implicit
2309 * shutdown of remote nodes by virtue of their connection stopping.
2310 */
2311 bool
pe__shutdown_requested(pe_node_t * node)2312 pe__shutdown_requested(pe_node_t *node)
2313 {
2314 const char *shutdown = pe_node_attribute_raw(node, XML_CIB_ATTR_SHUTDOWN);
2315
2316 return !pcmk__str_eq(shutdown, "0", pcmk__str_null_matches);
2317 }
2318
2319 /*!
2320 * \internal
2321 * \brief Update a data set's "recheck by" time
2322 *
2323 * \param[in] recheck Epoch time when recheck should happen
2324 * \param[in,out] data_set Current working set
2325 */
2326 void
pe__update_recheck_time(time_t recheck,pe_working_set_t * data_set)2327 pe__update_recheck_time(time_t recheck, pe_working_set_t *data_set)
2328 {
2329 if ((recheck > get_effective_time(data_set))
2330 && ((data_set->recheck_by == 0)
2331 || (data_set->recheck_by > recheck))) {
2332 data_set->recheck_by = recheck;
2333 }
2334 }
2335
2336 /*!
2337 * \internal
2338 * \brief Wrapper for pe_unpack_nvpairs() using a cluster working set
2339 */
2340 void
pe__unpack_dataset_nvpairs(xmlNode * xml_obj,const char * set_name,pe_rule_eval_data_t * rule_data,GHashTable * hash,const char * always_first,gboolean overwrite,pe_working_set_t * data_set)2341 pe__unpack_dataset_nvpairs(xmlNode *xml_obj, const char *set_name,
2342 pe_rule_eval_data_t *rule_data, GHashTable *hash,
2343 const char *always_first, gboolean overwrite,
2344 pe_working_set_t *data_set)
2345 {
2346 crm_time_t *next_change = crm_time_new_undefined();
2347
2348 pe_eval_nvpairs(data_set->input, xml_obj, set_name, rule_data, hash,
2349 always_first, overwrite, next_change);
2350 if (crm_time_is_defined(next_change)) {
2351 time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change);
2352
2353 pe__update_recheck_time(recheck, data_set);
2354 }
2355 crm_time_free(next_change);
2356 }
2357
2358 bool
pe__resource_is_disabled(pe_resource_t * rsc)2359 pe__resource_is_disabled(pe_resource_t *rsc)
2360 {
2361 const char *target_role = NULL;
2362
2363 CRM_CHECK(rsc != NULL, return false);
2364 target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
2365 if (target_role) {
2366 enum rsc_role_e target_role_e = text2role(target_role);
2367
2368 if ((target_role_e == RSC_ROLE_STOPPED)
2369 || ((target_role_e == RSC_ROLE_UNPROMOTED)
2370 && pcmk_is_set(uber_parent(rsc)->flags, pe_rsc_promotable))) {
2371 return true;
2372 }
2373 }
2374 return false;
2375 }
2376
2377 /*!
2378 * \internal
2379 * \brief Create an action to clear a resource's history from CIB
2380 *
2381 * \param[in] rsc Resource to clear
2382 * \param[in] node Node to clear history on
2383 *
2384 * \return New action to clear resource history
2385 */
2386 pe_action_t *
pe__clear_resource_history(pe_resource_t * rsc,pe_node_t * node,pe_working_set_t * data_set)2387 pe__clear_resource_history(pe_resource_t *rsc, pe_node_t *node,
2388 pe_working_set_t *data_set)
2389 {
2390 char *key = NULL;
2391
2392 CRM_ASSERT(rsc && node);
2393 key = pcmk__op_key(rsc->id, CRM_OP_LRM_DELETE, 0);
2394 return custom_action(rsc, key, CRM_OP_LRM_DELETE, node, FALSE, TRUE,
2395 data_set);
2396 }
2397
2398 bool
pe__rsc_running_on_any(pe_resource_t * rsc,GList * node_list)2399 pe__rsc_running_on_any(pe_resource_t *rsc, GList *node_list)
2400 {
2401 for (GList *ele = rsc->running_on; ele; ele = ele->next) {
2402 pe_node_t *node = (pe_node_t *) ele->data;
2403 if (pcmk__str_in_list(node_list, node->details->uname, pcmk__str_casei)) {
2404 return true;
2405 }
2406 }
2407
2408 return false;
2409 }
2410
2411 bool
pcmk__rsc_filtered_by_node(pe_resource_t * rsc,GList * only_node)2412 pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node)
2413 {
2414 return (rsc->fns->active(rsc, FALSE) && !pe__rsc_running_on_any(rsc, only_node));
2415 }
2416
2417 GList *
pe__filter_rsc_list(GList * rscs,GList * filter)2418 pe__filter_rsc_list(GList *rscs, GList *filter)
2419 {
2420 GList *retval = NULL;
2421
2422 for (GList *gIter = rscs; gIter; gIter = gIter->next) {
2423 pe_resource_t *rsc = (pe_resource_t *) gIter->data;
2424
2425 /* I think the second condition is safe here for all callers of this
2426 * function. If not, it needs to move into pe__node_text.
2427 */
2428 if (pcmk__str_in_list(filter, rsc_printable_id(rsc), pcmk__str_none) ||
2429 (rsc->parent && pcmk__str_in_list(filter, rsc_printable_id(rsc->parent), pcmk__str_none))) {
2430 retval = g_list_prepend(retval, rsc);
2431 }
2432 }
2433
2434 return retval;
2435 }
2436
2437 GList *
pe__build_node_name_list(pe_working_set_t * data_set,const char * s)2438 pe__build_node_name_list(pe_working_set_t *data_set, const char *s) {
2439 GList *nodes = NULL;
2440
2441 if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
2442 /* Nothing was given so return a list of all node names. Or, '*' was
2443 * given. This would normally fall into the pe__unames_with_tag branch
2444 * where it will return an empty list. Catch it here instead.
2445 */
2446 nodes = g_list_prepend(nodes, strdup("*"));
2447 } else {
2448 pe_node_t *node = pe_find_node(data_set->nodes, s);
2449
2450 if (node) {
2451 /* The given string was a valid uname for a node. Return a
2452 * singleton list containing just that uname.
2453 */
2454 nodes = g_list_prepend(nodes, strdup(s));
2455 } else {
2456 /* The given string was not a valid uname. It's either a tag or
2457 * it's a typo or something. In the first case, we'll return a
2458 * list of all the unames of the nodes with the given tag. In the
2459 * second case, we'll return a NULL pointer and nothing will
2460 * get displayed.
2461 */
2462 nodes = pe__unames_with_tag(data_set, s);
2463 }
2464 }
2465
2466 return nodes;
2467 }
2468
2469 GList *
pe__build_rsc_list(pe_working_set_t * data_set,const char * s)2470 pe__build_rsc_list(pe_working_set_t *data_set, const char *s) {
2471 GList *resources = NULL;
2472
2473 if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
2474 resources = g_list_prepend(resources, strdup("*"));
2475 } else {
2476 pe_resource_t *rsc = pe_find_resource_with_flags(data_set->resources, s,
2477 pe_find_renamed|pe_find_any);
2478
2479 if (rsc) {
2480 /* A colon in the name we were given means we're being asked to filter
2481 * on a specific instance of a cloned resource. Put that exact string
2482 * into the filter list. Otherwise, use the printable ID of whatever
2483 * resource was found that matches what was asked for.
2484 */
2485 if (strstr(s, ":") != NULL) {
2486 resources = g_list_prepend(resources, strdup(rsc->id));
2487 } else {
2488 resources = g_list_prepend(resources, strdup(rsc_printable_id(rsc)));
2489 }
2490 } else {
2491 /* The given string was not a valid resource name. It's either
2492 * a tag or it's a typo or something. See build_uname_list for
2493 * more detail.
2494 */
2495 resources = pe__rscs_with_tag(data_set, s);
2496 }
2497 }
2498
2499 return resources;
2500 }
2501