This source file includes following definitions.
- xe_interval
- is_op_dup
- op_cannot_recur
- is_recurring_history
- active_recurring_should_be_optional
- recurring_op_for_active
- cancel_if_running
- order_after_probes
- order_after_stops
- recurring_op_for_inactive
- pcmk__create_recurring_actions
- pcmk__new_cancel_action
- pcmk__schedule_cancel
- pcmk__reschedule_recurring
- pcmk__action_is_recurring
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdbool.h>
13
14 #include <crm/common/xml.h>
15 #include <crm/common/scheduler_internal.h>
16 #include <pacemaker-internal.h>
17
18 #include "libpacemaker_private.h"
19
20
21 struct op_history {
22
23 const char *id;
24 const char *name;
25
26
27 char *key;
28 enum rsc_role_e role;
29 guint interval_ms;
30 };
31
32
33
34
35
36
37
38
39
40 static guint
41 xe_interval(const xmlNode *xml)
42 {
43 guint interval_ms = 0U;
44
45 pcmk_parse_interval_spec(crm_element_value(xml, PCMK_META_INTERVAL),
46 &interval_ms);
47 return interval_ms;
48 }
49
50
51
52
53
54
55
56
57
58
59
60
61 static bool
62 is_op_dup(const pcmk_resource_t *rsc, const char *name, guint interval_ms)
63 {
64 const char *id = NULL;
65
66 for (xmlNode *op = pcmk__xe_first_child(rsc->priv->ops_xml, PCMK_XE_OP,
67 NULL, NULL);
68 op != NULL; op = pcmk__xe_next(op, PCMK_XE_OP)) {
69
70
71 if (!pcmk__str_eq(crm_element_value(op, PCMK_XA_NAME), name,
72 pcmk__str_none)
73 || (xe_interval(op) != interval_ms)) {
74 continue;
75 }
76
77 if (pcmk__xe_id(op) == NULL) {
78 continue;
79 }
80
81 if (id == NULL) {
82 id = pcmk__xe_id(op);
83 } else {
84 pcmk__config_err("Operation %s is duplicate of %s (do not use "
85 "same name and interval combination more "
86 "than once per resource)", pcmk__xe_id(op), id);
87 return true;
88 }
89 }
90 return false;
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 static bool
110 op_cannot_recur(const char *name)
111 {
112 return pcmk__str_any_of(name, PCMK_ACTION_STOP, PCMK_ACTION_START,
113 PCMK_ACTION_DEMOTE, PCMK_ACTION_PROMOTE,
114 PCMK_ACTION_RELOAD_AGENT,
115 PCMK_ACTION_MIGRATE_TO, PCMK_ACTION_MIGRATE_FROM,
116 NULL);
117 }
118
119
120
121
122
123
124
125
126
127
128
129 static bool
130 is_recurring_history(const pcmk_resource_t *rsc, const xmlNode *xml,
131 struct op_history *op)
132 {
133 const char *role = NULL;
134
135 op->interval_ms = xe_interval(xml);
136 if (op->interval_ms == 0) {
137 return false;
138 }
139
140 op->id = pcmk__xe_id(xml);
141 if (pcmk__str_empty(op->id)) {
142 pcmk__config_err("Ignoring resource history entry without ID");
143 return false;
144 }
145
146 op->name = crm_element_value(xml, PCMK_XA_NAME);
147 if (op_cannot_recur(op->name)) {
148 pcmk__config_err("Ignoring %s because %s action cannot be recurring",
149 op->id, pcmk__s(op->name, "unnamed"));
150 return false;
151 }
152
153
154 if (is_op_dup(rsc, op->name, op->interval_ms)) {
155 return false;
156 }
157
158
159 role = crm_element_value(xml, PCMK_XA_ROLE);
160 if (role == NULL) {
161 op->role = pcmk_role_unknown;
162 } else {
163 op->role = pcmk_parse_role(role);
164 if (op->role == pcmk_role_unknown) {
165 pcmk__config_err("Ignoring %s role because %s is not a valid role",
166 op->id, role);
167 return false;
168 }
169 }
170
171
172 if (pcmk__find_action_config(rsc, op->name, op->interval_ms,
173 false) == NULL) {
174 pcmk__rsc_trace(rsc,
175 "Ignoring %s (%s-interval %s for %s) because it is "
176 "disabled or no longer in configuration",
177 op->id, pcmk__readable_interval(op->interval_ms),
178 op->name, rsc->id);
179 return false;
180 }
181
182 op->key = pcmk__op_key(rsc->id, op->name, op->interval_ms);
183 return true;
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197 static bool
198 active_recurring_should_be_optional(const pcmk_resource_t *rsc,
199 const pcmk_node_t *node, const char *key,
200 pcmk_action_t *start)
201 {
202 GList *possible_matches = NULL;
203
204 if (node == NULL) {
205 pcmk__rsc_trace(rsc,
206 "%s will be mandatory because resource is unmanaged",
207 key);
208 return false;
209 }
210
211 if (!pcmk_is_set(rsc->priv->cmds->action_flags(start, NULL),
212 pcmk__action_optional)) {
213 pcmk__rsc_trace(rsc, "%s will be mandatory because %s is",
214 key, start->uuid);
215 return false;
216 }
217
218 possible_matches = find_actions_exact(rsc->priv->actions, key, node);
219 if (possible_matches == NULL) {
220 pcmk__rsc_trace(rsc,
221 "%s will be mandatory because it is not active on %s",
222 key, pcmk__node_name(node));
223 return false;
224 }
225
226 for (const GList *iter = possible_matches;
227 iter != NULL; iter = iter->next) {
228
229 const pcmk_action_t *op = (const pcmk_action_t *) iter->data;
230
231 if (pcmk_is_set(op->flags, pcmk__action_reschedule)) {
232 pcmk__rsc_trace(rsc,
233 "%s will be mandatory because "
234 "it needs to be rescheduled", key);
235 g_list_free(possible_matches);
236 return false;
237 }
238 }
239
240 g_list_free(possible_matches);
241 return true;
242 }
243
244
245
246
247
248
249
250
251
252
253 static void
254 recurring_op_for_active(pcmk_resource_t *rsc, pcmk_action_t *start,
255 const pcmk_node_t *node, const struct op_history *op)
256 {
257 pcmk_action_t *mon = NULL;
258 bool is_optional = true;
259 bool role_match = false;
260 enum rsc_role_e monitor_role = op->role;
261
262
263 if (monitor_role == pcmk_role_stopped) {
264 return;
265 }
266
267 is_optional = active_recurring_should_be_optional(rsc, node, op->key,
268 start);
269
270
271 if (monitor_role == pcmk_role_unknown) {
272 monitor_role = pcmk_role_unpromoted;
273 role_match = (rsc->priv->next_role != pcmk_role_promoted);
274 } else {
275 role_match = (rsc->priv->next_role == monitor_role);
276 }
277
278 if (!role_match) {
279 if (is_optional) {
280 char *after_key = NULL;
281 pcmk_action_t *cancel_op = pcmk__new_cancel_action(rsc, op->name,
282 op->interval_ms,
283 node);
284
285 switch (rsc->priv->orig_role) {
286 case pcmk_role_unpromoted:
287 case pcmk_role_started:
288 if (rsc->priv->next_role == pcmk_role_promoted) {
289 after_key = promote_key(rsc);
290
291 } else if (rsc->priv->next_role == pcmk_role_stopped) {
292 after_key = stop_key(rsc);
293 }
294
295 break;
296 case pcmk_role_promoted:
297 after_key = demote_key(rsc);
298 break;
299 default:
300 break;
301 }
302
303 if (after_key) {
304 pcmk__new_ordering(rsc, NULL, cancel_op, rsc, after_key, NULL,
305 pcmk__ar_unrunnable_first_blocks,
306 rsc->priv->scheduler);
307 }
308 }
309
310 do_crm_log((is_optional? LOG_INFO : LOG_TRACE),
311 "%s recurring action %s because %s configured for %s role "
312 "(not %s)",
313 (is_optional? "Cancelling" : "Ignoring"), op->key, op->id,
314 pcmk_role_text(monitor_role),
315 pcmk_role_text(rsc->priv->next_role));
316 return;
317 }
318
319 pcmk__rsc_trace(rsc,
320 "Creating %s recurring action %s for %s (%s %s on %s)",
321 (is_optional? "optional" : "mandatory"), op->key,
322 op->id, rsc->id, pcmk_role_text(rsc->priv->next_role),
323 pcmk__node_name(node));
324
325 mon = custom_action(rsc, strdup(op->key), op->name, node, is_optional,
326 rsc->priv->scheduler);
327
328 if (!pcmk_is_set(start->flags, pcmk__action_runnable)) {
329 pcmk__rsc_trace(rsc, "%s is unrunnable because start is", mon->uuid);
330 pcmk__clear_action_flags(mon, pcmk__action_runnable);
331
332 } else if ((node == NULL) || !node->details->online
333 || node->details->unclean) {
334 pcmk__rsc_trace(rsc, "%s is unrunnable because no node is available",
335 mon->uuid);
336 pcmk__clear_action_flags(mon, pcmk__action_runnable);
337
338 } else if (!pcmk_is_set(mon->flags, pcmk__action_optional)) {
339 pcmk__rsc_info(rsc, "Start %s-interval %s for %s on %s",
340 pcmk__readable_interval(op->interval_ms), mon->task,
341 rsc->id, pcmk__node_name(node));
342 }
343
344 if (rsc->priv->next_role == pcmk_role_promoted) {
345 pe__add_action_expected_result(mon, CRM_EX_PROMOTED);
346 }
347
348
349 if ((node == NULL) || pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
350 pcmk__new_ordering(rsc, start_key(rsc), NULL,
351 NULL, strdup(mon->uuid), mon,
352 pcmk__ar_first_implies_then
353 |pcmk__ar_unrunnable_first_blocks,
354 rsc->priv->scheduler);
355
356 pcmk__new_ordering(rsc, reload_key(rsc), NULL,
357 NULL, strdup(mon->uuid), mon,
358 pcmk__ar_first_implies_then
359 |pcmk__ar_unrunnable_first_blocks,
360 rsc->priv->scheduler);
361
362 if (rsc->priv->next_role == pcmk_role_promoted) {
363 pcmk__new_ordering(rsc, promote_key(rsc), NULL,
364 rsc, NULL, mon,
365 pcmk__ar_ordered
366 |pcmk__ar_unrunnable_first_blocks,
367 rsc->priv->scheduler);
368
369 } else if (rsc->priv->orig_role == pcmk_role_promoted) {
370 pcmk__new_ordering(rsc, demote_key(rsc), NULL,
371 rsc, NULL, mon,
372 pcmk__ar_ordered
373 |pcmk__ar_unrunnable_first_blocks,
374 rsc->priv->scheduler);
375 }
376 }
377 }
378
379
380
381
382
383
384
385
386
387
388
389 static void
390 cancel_if_running(pcmk_resource_t *rsc, const pcmk_node_t *node,
391 const char *key, const char *name, guint interval_ms)
392 {
393 GList *possible_matches = find_actions_exact(rsc->priv->actions, key,
394 node);
395 pcmk_action_t *cancel_op = NULL;
396
397 if (possible_matches == NULL) {
398 return;
399 }
400 g_list_free(possible_matches);
401
402 cancel_op = pcmk__new_cancel_action(rsc, name, interval_ms, node);
403
404 switch (rsc->priv->next_role) {
405 case pcmk_role_started:
406 case pcmk_role_unpromoted:
407
408
409
410
411
412 pcmk__new_ordering(rsc, NULL, cancel_op,
413 rsc, start_key(rsc), NULL,
414 pcmk__ar_unrunnable_first_blocks,
415 rsc->priv->scheduler);
416 break;
417 default:
418 break;
419 }
420 pcmk__rsc_info(rsc,
421 "Cancelling %s-interval %s action for %s on %s because "
422 "configured for " PCMK_ROLE_STOPPED " role (not %s)",
423 pcmk__readable_interval(interval_ms), name, rsc->id,
424 pcmk__node_name(node),
425 pcmk_role_text(rsc->priv->next_role));
426 }
427
428
429
430
431
432
433
434
435
436 static void
437 order_after_probes(pcmk_resource_t *rsc, const pcmk_node_t *node,
438 pcmk_action_t *action)
439 {
440 GList *probes = pe__resource_actions(rsc, node, PCMK_ACTION_MONITOR, FALSE);
441
442 for (GList *iter = probes; iter != NULL; iter = iter->next) {
443 order_actions((pcmk_action_t *) iter->data, action,
444 pcmk__ar_unrunnable_first_blocks);
445 }
446 g_list_free(probes);
447 }
448
449
450
451
452
453
454
455
456
457 static void
458 order_after_stops(pcmk_resource_t *rsc, const pcmk_node_t *node,
459 pcmk_action_t *action)
460 {
461 GList *stop_ops = pe__resource_actions(rsc, node, PCMK_ACTION_STOP, TRUE);
462
463 for (GList *iter = stop_ops; iter != NULL; iter = iter->next) {
464 pcmk_action_t *stop = (pcmk_action_t *) iter->data;
465
466 if (!pcmk_is_set(stop->flags, pcmk__action_optional)
467 && !pcmk_is_set(action->flags, pcmk__action_optional)
468 && !pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
469 pcmk__rsc_trace(rsc, "%s optional on %s: unmanaged",
470 action->uuid, pcmk__node_name(node));
471 pcmk__set_action_flags(action, pcmk__action_optional);
472 }
473
474 if (!pcmk_is_set(stop->flags, pcmk__action_runnable)) {
475 crm_debug("%s unrunnable on %s: stop is unrunnable",
476 action->uuid, pcmk__node_name(node));
477 pcmk__clear_action_flags(action, pcmk__action_runnable);
478 }
479
480 if (pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
481 pcmk__new_ordering(rsc, stop_key(rsc), stop,
482 NULL, NULL, action,
483 pcmk__ar_first_implies_then
484 |pcmk__ar_unrunnable_first_blocks,
485 rsc->priv->scheduler);
486 }
487 }
488 g_list_free(stop_ops);
489 }
490
491
492
493
494
495
496
497
498
499 static void
500 recurring_op_for_inactive(pcmk_resource_t *rsc, const pcmk_node_t *node,
501 const struct op_history *op)
502 {
503 GList *possible_matches = NULL;
504
505
506 if (op->role != pcmk_role_stopped) {
507 return;
508 }
509
510 if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)) {
511 crm_notice("Ignoring %s (recurring monitors for " PCMK_ROLE_STOPPED
512 " role are not supported for anonymous clones)", op->id);
513 return;
514 }
515
516 pcmk__rsc_trace(rsc,
517 "Creating recurring action %s for %s on nodes "
518 "where it should not be running", op->id, rsc->id);
519
520 for (GList *iter = rsc->priv->scheduler->nodes;
521 iter != NULL; iter = iter->next) {
522
523 pcmk_node_t *stop_node = (pcmk_node_t *) iter->data;
524
525 bool is_optional = true;
526 pcmk_action_t *stopped_mon = NULL;
527
528
529 if ((node != NULL)
530 && pcmk__str_eq(stop_node->priv->name, node->priv->name,
531 pcmk__str_casei)) {
532 cancel_if_running(rsc, node, op->key, op->name, op->interval_ms);
533 continue;
534 }
535
536
537 possible_matches = find_actions_exact(rsc->priv->actions, op->key,
538 stop_node);
539 is_optional = (possible_matches != NULL);
540 g_list_free(possible_matches);
541
542 pcmk__rsc_trace(rsc,
543 "Creating %s recurring action %s for %s (%s "
544 PCMK_ROLE_STOPPED " on %s)",
545 (is_optional? "optional" : "mandatory"),
546 op->key, op->id, rsc->id, pcmk__node_name(stop_node));
547
548 stopped_mon = custom_action(rsc, strdup(op->key), op->name, stop_node,
549 is_optional, rsc->priv->scheduler);
550
551 pe__add_action_expected_result(stopped_mon, CRM_EX_NOT_RUNNING);
552
553 if (pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
554 order_after_probes(rsc, stop_node, stopped_mon);
555 }
556
557
558
559
560 order_after_stops(rsc, stop_node, stopped_mon);
561
562 if (!stop_node->details->online || stop_node->details->unclean) {
563 pcmk__rsc_debug(rsc, "%s unrunnable on %s: node unavailable)",
564 stopped_mon->uuid, pcmk__node_name(stop_node));
565 pcmk__clear_action_flags(stopped_mon, pcmk__action_runnable);
566 }
567
568 if (pcmk_is_set(stopped_mon->flags, pcmk__action_runnable)
569 && !pcmk_is_set(stopped_mon->flags, pcmk__action_optional)) {
570 crm_notice("Start recurring %s-interval %s for "
571 PCMK_ROLE_STOPPED " %s on %s",
572 pcmk__readable_interval(op->interval_ms),
573 stopped_mon->task, rsc->id, pcmk__node_name(stop_node));
574 }
575 }
576 }
577
578
579
580
581
582
583
584 void
585 pcmk__create_recurring_actions(pcmk_resource_t *rsc)
586 {
587 pcmk_action_t *start = NULL;
588
589 if (pcmk_is_set(rsc->flags, pcmk__rsc_blocked)) {
590 pcmk__rsc_trace(rsc,
591 "Skipping recurring actions for blocked resource %s",
592 rsc->id);
593 return;
594 }
595
596 if (pcmk_is_set(rsc->flags, pcmk__rsc_maintenance)) {
597 pcmk__rsc_trace(rsc,
598 "Skipping recurring actions for %s "
599 "in maintenance mode", rsc->id);
600 return;
601 }
602
603 if (rsc->priv->assigned_node == NULL) {
604
605
606 } else if (rsc->priv->assigned_node->details->maintenance) {
607 pcmk__rsc_trace(rsc,
608 "Skipping recurring actions for %s on %s "
609 "in maintenance mode",
610 rsc->id, pcmk__node_name(rsc->priv->assigned_node));
611
612 } else if ((rsc->priv->next_role != pcmk_role_stopped)
613 || !pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
614
615 start = start_action(rsc, rsc->priv->assigned_node, TRUE);
616 }
617
618 pcmk__rsc_trace(rsc, "Creating any recurring actions needed for %s",
619 rsc->id);
620
621 for (xmlNode *op = pcmk__xe_first_child(rsc->priv->ops_xml, PCMK_XE_OP,
622 NULL, NULL);
623 op != NULL; op = pcmk__xe_next(op, PCMK_XE_OP)) {
624
625 struct op_history op_history = { NULL, };
626
627 if (!is_recurring_history(rsc, op, &op_history)) {
628 continue;
629 }
630
631 if (start != NULL) {
632 recurring_op_for_active(rsc, start, rsc->priv->assigned_node,
633 &op_history);
634 }
635 recurring_op_for_inactive(rsc, rsc->priv->assigned_node,
636 &op_history);
637
638 free(op_history.key);
639 }
640 }
641
642
643
644
645
646
647
648
649
650
651
652
653 pcmk_action_t *
654 pcmk__new_cancel_action(pcmk_resource_t *rsc, const char *task,
655 guint interval_ms, const pcmk_node_t *node)
656 {
657 pcmk_action_t *cancel_op = NULL;
658 char *key = NULL;
659 char *interval_ms_s = NULL;
660
661 pcmk__assert((rsc != NULL) && (task != NULL) && (node != NULL));
662
663 key = pcmk__op_key(rsc->id, task, interval_ms);
664
665
666
667
668 cancel_op = custom_action(rsc, key, PCMK_ACTION_CANCEL, node, FALSE,
669 rsc->priv->scheduler);
670
671 pcmk__str_update(&(cancel_op->task), PCMK_ACTION_CANCEL);
672 pcmk__str_update(&(cancel_op->cancel_task), task);
673
674 interval_ms_s = crm_strdup_printf("%u", interval_ms);
675 pcmk__insert_meta(cancel_op, PCMK_XA_OPERATION, task);
676 pcmk__insert_meta(cancel_op, PCMK_META_INTERVAL, interval_ms_s);
677 free(interval_ms_s);
678
679 return cancel_op;
680 }
681
682
683
684
685
686
687
688
689
690
691
692
693 void
694 pcmk__schedule_cancel(pcmk_resource_t *rsc, const char *call_id,
695 const char *task, guint interval_ms,
696 const pcmk_node_t *node, const char *reason)
697 {
698 pcmk_action_t *cancel = NULL;
699
700 CRM_CHECK((rsc != NULL) && (task != NULL)
701 && (node != NULL) && (reason != NULL),
702 return);
703
704 crm_info("Recurring %s-interval %s for %s will be stopped on %s: %s",
705 pcmk__readable_interval(interval_ms), task, rsc->id,
706 pcmk__node_name(node), reason);
707 cancel = pcmk__new_cancel_action(rsc, task, interval_ms, node);
708 pcmk__insert_meta(cancel, PCMK__XA_CALL_ID, call_id);
709
710
711 pcmk__new_ordering(rsc, stop_key(rsc), NULL, rsc, NULL, cancel,
712 pcmk__ar_ordered, rsc->priv->scheduler);
713 }
714
715
716
717
718
719
720
721
722
723
724 void
725 pcmk__reschedule_recurring(pcmk_resource_t *rsc, const char *task,
726 guint interval_ms, pcmk_node_t *node)
727 {
728 pcmk_action_t *op = NULL;
729
730 trigger_unfencing(rsc, node, "Device parameters changed (reschedule)",
731 NULL, rsc->priv->scheduler);
732 op = custom_action(rsc, pcmk__op_key(rsc->id, task, interval_ms),
733 task, node, TRUE, rsc->priv->scheduler);
734 pcmk__set_action_flags(op, pcmk__action_reschedule);
735 }
736
737
738
739
740
741
742
743
744
745 bool
746 pcmk__action_is_recurring(const pcmk_action_t *action)
747 {
748 guint interval_ms = 0;
749
750 if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0,
751 &interval_ms) != pcmk_rc_ok) {
752 return false;
753 }
754 return (interval_ms > 0);
755 }