This source file includes following definitions.
- pe__clone_max
- pe__clone_node_max
- pe__clone_promoted_max
- pe__clone_promoted_node_max
- sorted_hash_table_values
- nodes_with_status
- node_list_to_str
- clone_header
- pe__force_anon
- find_clone_instance
- pe__create_clone_child
- unpack_meta_int
- clone_unpack
- clone_active
- short_print
- configured_role_str
- configured_role
- clone_print_xml
- is_set_recursive
- clone_print
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- clone_free
- clone_resource_state
- pe__is_universal_clone
- pe__clone_is_filtered
- pe__clone_child_id
- pe__clone_is_ordered
- pe__set_clone_flag
- pe__clone_flag_is_set
- pe__create_promotable_pseudo_ops
- pe__create_clone_notifications
- pe__free_clone_notification_data
- pe__create_clone_notif_pseudo_ops
- pe__clone_max_per_node
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdint.h>
13
14 #include <crm/pengine/rules.h>
15 #include <crm/pengine/status.h>
16 #include <crm/pengine/internal.h>
17 #include <pe_status_private.h>
18 #include <crm/common/xml.h>
19 #include <crm/common/output.h>
20 #include <crm/common/xml_internal.h>
21 #include <crm/common/scheduler_internal.h>
22
23 #ifdef PCMK__COMPAT_2_0
24 #define PROMOTED_INSTANCES PCMK__ROLE_PROMOTED_LEGACY "s"
25 #define UNPROMOTED_INSTANCES PCMK__ROLE_UNPROMOTED_LEGACY "s"
26 #else
27 #define PROMOTED_INSTANCES PCMK_ROLE_PROMOTED
28 #define UNPROMOTED_INSTANCES PCMK_ROLE_UNPROMOTED
29 #endif
30
31 typedef struct clone_variant_data_s {
32 int clone_max;
33 int clone_node_max;
34
35 int promoted_max;
36 int promoted_node_max;
37
38 int total_clones;
39
40 uint32_t flags;
41
42 notify_data_t *stop_notify;
43 notify_data_t *start_notify;
44 notify_data_t *demote_notify;
45 notify_data_t *promote_notify;
46
47 xmlNode *xml_obj_child;
48 } clone_variant_data_t;
49
50 #define get_clone_variant_data(data, rsc) \
51 pcmk__assert(pcmk__is_clone(rsc) && (rsc->variant_opaque != NULL)); \
52 data = (clone_variant_data_t *) rsc->variant_opaque;
53
54
55
56
57
58
59
60
61
62 int
63 pe__clone_max(const pcmk_resource_t *clone)
64 {
65 const clone_variant_data_t *clone_data = NULL;
66
67 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
68 return clone_data->clone_max;
69 }
70
71
72
73
74
75
76
77
78
79 int
80 pe__clone_node_max(const pcmk_resource_t *clone)
81 {
82 const clone_variant_data_t *clone_data = NULL;
83
84 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
85 return clone_data->clone_node_max;
86 }
87
88
89
90
91
92
93
94
95
96 int
97 pe__clone_promoted_max(const pcmk_resource_t *clone)
98 {
99 clone_variant_data_t *clone_data = NULL;
100
101 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
102 return clone_data->promoted_max;
103 }
104
105
106
107
108
109
110
111
112
113 int
114 pe__clone_promoted_node_max(const pcmk_resource_t *clone)
115 {
116 clone_variant_data_t *clone_data = NULL;
117
118 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
119 return clone_data->promoted_node_max;
120 }
121
122 static GList *
123 sorted_hash_table_values(GHashTable *table)
124 {
125 GList *retval = NULL;
126 GHashTableIter iter;
127 gpointer key, value;
128
129 g_hash_table_iter_init(&iter, table);
130 while (g_hash_table_iter_next(&iter, &key, &value)) {
131 if (!g_list_find_custom(retval, value, (GCompareFunc) strcmp)) {
132 retval = g_list_prepend(retval, (char *) value);
133 }
134 }
135
136 retval = g_list_sort(retval, (GCompareFunc) strcmp);
137 return retval;
138 }
139
140 static GList *
141 nodes_with_status(GHashTable *table, const char *status)
142 {
143 GList *retval = NULL;
144 GHashTableIter iter;
145 gpointer key, value;
146
147 g_hash_table_iter_init(&iter, table);
148 while (g_hash_table_iter_next(&iter, &key, &value)) {
149 if (!strcmp((char *) value, status)) {
150 retval = g_list_prepend(retval, key);
151 }
152 }
153
154 retval = g_list_sort(retval, (GCompareFunc) pcmk__numeric_strcasecmp);
155 return retval;
156 }
157
158 static GString *
159 node_list_to_str(const GList *list)
160 {
161 GString *retval = NULL;
162
163 for (const GList *iter = list; iter != NULL; iter = iter->next) {
164 pcmk__add_word(&retval, 1024, (const char *) iter->data);
165 }
166
167 return retval;
168 }
169
170 static void
171 clone_header(pcmk__output_t *out, int *rc, const pcmk_resource_t *rsc,
172 clone_variant_data_t *clone_data, const char *desc)
173 {
174 GString *attrs = NULL;
175
176 if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
177 pcmk__add_separated_word(&attrs, 64, "promotable", ", ");
178 }
179
180 if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
181 pcmk__add_separated_word(&attrs, 64, "unique", ", ");
182 }
183
184 if (pe__resource_is_disabled(rsc)) {
185 pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
186 }
187
188 if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
189 pcmk__add_separated_word(&attrs, 64, "maintenance", ", ");
190
191 } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
192 pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
193 }
194
195 if (attrs != NULL) {
196 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)%s%s%s",
197 rsc->id,
198 pcmk__xe_id(clone_data->xml_obj_child),
199 (const char *) attrs->str, desc ? " (" : "",
200 desc ? desc : "", desc ? ")" : "");
201 g_string_free(attrs, TRUE);
202 } else {
203 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]%s%s%s",
204 rsc->id,
205 pcmk__xe_id(clone_data->xml_obj_child),
206 desc ? " (" : "", desc ? desc : "",
207 desc ? ")" : "");
208 }
209 }
210
211 void
212 pe__force_anon(const char *standard, pcmk_resource_t *rsc, const char *rid,
213 pcmk_scheduler_t *scheduler)
214 {
215 if (pcmk__is_clone(rsc)) {
216 clone_variant_data_t *clone_data = rsc->variant_opaque;
217
218 pcmk__config_warn("Ignoring " PCMK_META_GLOBALLY_UNIQUE " for %s "
219 "because %s resources such as %s can be used only as "
220 "anonymous clones", rsc->id, standard, rid);
221
222 clone_data->clone_node_max = 1;
223 clone_data->clone_max = QB_MIN(clone_data->clone_max,
224 g_list_length(scheduler->nodes));
225 }
226 }
227
228 pcmk_resource_t *
229 find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
230 {
231 char *child_id = NULL;
232 pcmk_resource_t *child = NULL;
233 const char *child_base = NULL;
234 clone_variant_data_t *clone_data = NULL;
235
236 get_clone_variant_data(clone_data, rsc);
237
238 child_base = pcmk__xe_id(clone_data->xml_obj_child);
239 child_id = crm_strdup_printf("%s:%s", child_base, sub_id);
240 child = pe_find_resource(rsc->children, child_id);
241
242 free(child_id);
243 return child;
244 }
245
246 pcmk_resource_t *
247 pe__create_clone_child(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
248 {
249 gboolean as_orphan = FALSE;
250 char *inc_num = NULL;
251 char *inc_max = NULL;
252 pcmk_resource_t *child_rsc = NULL;
253 xmlNode *child_copy = NULL;
254 clone_variant_data_t *clone_data = NULL;
255
256 get_clone_variant_data(clone_data, rsc);
257
258 CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
259
260 if (clone_data->total_clones >= clone_data->clone_max) {
261
262 as_orphan = TRUE;
263 }
264
265
266 inc_num = pcmk__itoa(clone_data->total_clones);
267 inc_max = pcmk__itoa(clone_data->clone_max);
268
269 child_copy = pcmk__xml_copy(NULL, clone_data->xml_obj_child);
270
271 crm_xml_add(child_copy, PCMK__META_CLONE, inc_num);
272
273 if (pe__unpack_resource(child_copy, &child_rsc, rsc,
274 scheduler) != pcmk_rc_ok) {
275 goto bail;
276 }
277
278
279 pcmk__assert(child_rsc != NULL);
280 clone_data->total_clones += 1;
281 pcmk__rsc_trace(child_rsc, "Setting clone attributes for: %s",
282 child_rsc->id);
283 rsc->children = g_list_append(rsc->children, child_rsc);
284 if (as_orphan) {
285 pe__set_resource_flags_recursive(child_rsc, pcmk_rsc_removed);
286 }
287
288 pcmk__insert_meta(child_rsc, PCMK_META_CLONE_MAX, inc_max);
289 pcmk__rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
290
291 bail:
292 free(inc_num);
293 free(inc_max);
294
295 return child_rsc;
296 }
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311 static int
312 unpack_meta_int(const pcmk_resource_t *rsc, const char *meta_name,
313 const char *deprecated_name, int default_value)
314 {
315 int integer = default_value;
316 const char *value = g_hash_table_lookup(rsc->meta, meta_name);
317
318 if ((value == NULL) && (deprecated_name != NULL)) {
319 value = g_hash_table_lookup(rsc->meta, deprecated_name);
320
321 if (value != NULL) {
322 if (pcmk__str_eq(deprecated_name, PCMK__META_PROMOTED_MAX_LEGACY,
323 pcmk__str_none)) {
324 pcmk__warn_once(pcmk__wo_clone_master_max,
325 "Support for the " PCMK__META_PROMOTED_MAX_LEGACY
326 " meta-attribute (such as in %s) is deprecated "
327 "and will be removed in a future release. Use the "
328 PCMK_META_PROMOTED_MAX " meta-attribute instead.",
329 rsc->id);
330 } else if (pcmk__str_eq(deprecated_name, PCMK__META_PROMOTED_NODE_MAX_LEGACY,
331 pcmk__str_none)) {
332 pcmk__warn_once(pcmk__wo_clone_master_node_max,
333 "Support for the " PCMK__META_PROMOTED_NODE_MAX_LEGACY
334 " meta-attribute (such as in %s) is deprecated "
335 "and will be removed in a future release. Use the "
336 PCMK_META_PROMOTED_NODE_MAX " meta-attribute instead.",
337 rsc->id);
338 }
339 }
340 }
341 if (value != NULL) {
342 pcmk__scan_min_int(value, &integer, 0);
343 }
344 return integer;
345 }
346
347 gboolean
348 clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
349 {
350 int lpc = 0;
351 xmlNode *a_child = NULL;
352 xmlNode *xml_obj = rsc->xml;
353 clone_variant_data_t *clone_data = NULL;
354
355 pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
356
357 clone_data = pcmk__assert_alloc(1, sizeof(clone_variant_data_t));
358 rsc->variant_opaque = clone_data;
359
360 if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
361
362
363 clone_data->promoted_max =
364 unpack_meta_int(rsc, PCMK_META_PROMOTED_MAX,
365 PCMK__META_PROMOTED_MAX_LEGACY, 1);
366
367
368
369 clone_data->promoted_node_max =
370 unpack_meta_int(rsc, PCMK_META_PROMOTED_NODE_MAX,
371 PCMK__META_PROMOTED_NODE_MAX_LEGACY, 1);
372 }
373
374
375 clone_data->clone_node_max = unpack_meta_int(rsc, PCMK_META_CLONE_NODE_MAX,
376 NULL, 1);
377
378
379
380
381 clone_data->clone_max = unpack_meta_int(rsc, PCMK_META_CLONE_MAX, NULL,
382 QB_MAX(1, g_list_length(scheduler->nodes)));
383
384 if (crm_is_true(g_hash_table_lookup(rsc->meta, PCMK_META_ORDERED))) {
385 clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
386 "Clone", rsc->id,
387 clone_data->flags,
388 pcmk__clone_ordered,
389 "pcmk__clone_ordered");
390 }
391
392 if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
393 && (clone_data->clone_node_max > 1)) {
394
395 pcmk__config_err("Ignoring " PCMK_META_CLONE_NODE_MAX " of %d for %s "
396 "because anonymous clones support only one instance "
397 "per node", clone_data->clone_node_max, rsc->id);
398 clone_data->clone_node_max = 1;
399 }
400
401 pcmk__rsc_trace(rsc, "Options for %s", rsc->id);
402 pcmk__rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
403 pcmk__rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
404 pcmk__rsc_trace(rsc, "\tClone is unique: %s",
405 pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
406 pcmk__rsc_trace(rsc, "\tClone is promotable: %s",
407 pcmk__flag_text(rsc->flags, pcmk_rsc_promotable));
408
409
410 for (a_child = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
411 a_child != NULL; a_child = pcmk__xe_next(a_child)) {
412
413 if (pcmk__str_any_of((const char *) a_child->name,
414 PCMK_XE_PRIMITIVE, PCMK_XE_GROUP, NULL)) {
415 clone_data->xml_obj_child = a_child;
416 break;
417 }
418 }
419
420 if (clone_data->xml_obj_child == NULL) {
421 pcmk__config_err("%s has nothing to clone", rsc->id);
422 return FALSE;
423 }
424
425
426
427
428
429
430
431 if (g_hash_table_lookup(rsc->meta, PCMK_META_RESOURCE_STICKINESS) == NULL) {
432 pcmk__insert_meta(rsc, PCMK_META_RESOURCE_STICKINESS, "1");
433 }
434
435
436
437
438
439 pcmk__insert_meta(rsc, PCMK_META_GLOBALLY_UNIQUE,
440 pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
441
442 if (clone_data->clone_max <= 0) {
443
444
445
446 if (pe__create_clone_child(rsc, scheduler) == NULL) {
447 return FALSE;
448 }
449
450 } else {
451
452 for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
453 if (pe__create_clone_child(rsc, scheduler) == NULL) {
454 return FALSE;
455 }
456 }
457 }
458
459 pcmk__rsc_trace(rsc, "Added %d children to resource %s...",
460 clone_data->clone_max, rsc->id);
461 return TRUE;
462 }
463
464 gboolean
465 clone_active(pcmk_resource_t * rsc, gboolean all)
466 {
467 GList *gIter = rsc->children;
468
469 for (; gIter != NULL; gIter = gIter->next) {
470 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
471 gboolean child_active = child_rsc->fns->active(child_rsc, all);
472
473 if (all == FALSE && child_active) {
474 return TRUE;
475 } else if (all && child_active == FALSE) {
476 return FALSE;
477 }
478 }
479
480 if (all) {
481 return TRUE;
482 } else {
483 return FALSE;
484 }
485 }
486
487
488
489
490
491 static void
492 short_print(const char *list, const char *prefix, const char *type,
493 const char *suffix, long options, void *print_data)
494 {
495 if(suffix == NULL) {
496 suffix = "";
497 }
498
499 if (!pcmk__str_empty(list)) {
500 if (options & pe_print_html) {
501 status_print("<li>");
502 }
503 status_print("%s%s: [ %s ]%s", prefix, type, list, suffix);
504
505 if (options & pe_print_html) {
506 status_print("</li>\n");
507
508 } else if (options & pe_print_suppres_nl) {
509
510 } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
511 status_print("\n");
512 }
513
514 }
515 }
516
517 static const char *
518 configured_role_str(pcmk_resource_t * rsc)
519 {
520 const char *target_role = g_hash_table_lookup(rsc->meta,
521 PCMK_META_TARGET_ROLE);
522
523 if ((target_role == NULL) && rsc->children && rsc->children->data) {
524 pcmk_resource_t *instance = rsc->children->data;
525
526 target_role = g_hash_table_lookup(instance->meta,
527 PCMK_META_TARGET_ROLE);
528 }
529 return target_role;
530 }
531
532 static enum rsc_role_e
533 configured_role(pcmk_resource_t *rsc)
534 {
535 enum rsc_role_e role = pcmk_role_unknown;
536 const char *target_role = configured_role_str(rsc);
537
538 if (target_role != NULL) {
539 role = pcmk_parse_role(target_role);
540 if (role == pcmk_role_unknown) {
541 pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
542 " for resource %s", rsc->id);
543 }
544 }
545 return role;
546 }
547
548
549
550
551
552 static void
553 clone_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
554 void *print_data)
555 {
556 char *child_text = crm_strdup_printf("%s ", pre_text);
557 const char *target_role = configured_role_str(rsc);
558 GList *gIter = rsc->children;
559
560 status_print("%s<clone ", pre_text);
561 status_print(PCMK_XA_ID "=\"%s\" ", rsc->id);
562 status_print("multi_state=\"%s\" ",
563 pcmk__flag_text(rsc->flags, pcmk_rsc_promotable));
564 status_print("unique=\"%s\" ",
565 pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
566 status_print("managed=\"%s\" ",
567 pcmk__flag_text(rsc->flags, pcmk_rsc_managed));
568 status_print("failed=\"%s\" ",
569 pcmk__flag_text(rsc->flags, pcmk_rsc_failed));
570 status_print("failure_ignored=\"%s\" ",
571 pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure));
572 if (target_role) {
573 status_print("target_role=\"%s\" ", target_role);
574 }
575 status_print(">\n");
576
577 for (; gIter != NULL; gIter = gIter->next) {
578 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
579
580 child_rsc->fns->print(child_rsc, child_text, options, print_data);
581 }
582
583 status_print("%s</clone>\n", pre_text);
584 free(child_text);
585 }
586
587 bool
588 is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any)
589 {
590 GList *gIter;
591 bool all = !any;
592
593 if (pcmk_is_set(rsc->flags, flag)) {
594 if(any) {
595 return TRUE;
596 }
597 } else if(all) {
598 return FALSE;
599 }
600
601 for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
602 if(is_set_recursive(gIter->data, flag, any)) {
603 if(any) {
604 return TRUE;
605 }
606
607 } else if(all) {
608 return FALSE;
609 }
610 }
611
612 if(all) {
613 return TRUE;
614 }
615 return FALSE;
616 }
617
618
619
620
621
622 void
623 clone_print(pcmk_resource_t *rsc, const char *pre_text, long options,
624 void *print_data)
625 {
626 GString *list_text = NULL;
627 char *child_text = NULL;
628 GString *stopped_list = NULL;
629
630 GList *promoted_list = NULL;
631 GList *started_list = NULL;
632 GList *gIter = rsc->children;
633
634 clone_variant_data_t *clone_data = NULL;
635 int active_instances = 0;
636
637 if (pre_text == NULL) {
638 pre_text = " ";
639 }
640
641 if (options & pe_print_xml) {
642 clone_print_xml(rsc, pre_text, options, print_data);
643 return;
644 }
645
646 get_clone_variant_data(clone_data, rsc);
647
648 child_text = crm_strdup_printf("%s ", pre_text);
649
650 status_print("%sClone Set: %s [%s]%s%s%s",
651 pcmk__s(pre_text, ""), rsc->id,
652 pcmk__xe_id(clone_data->xml_obj_child),
653 pcmk_is_set(rsc->flags, pcmk_rsc_promotable)? " (promotable)" : "",
654 pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
655 pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " (unmanaged)");
656
657 if (options & pe_print_html) {
658 status_print("\n<ul>\n");
659
660 } else if ((options & pe_print_log) == 0) {
661 status_print("\n");
662 }
663
664 for (; gIter != NULL; gIter = gIter->next) {
665 gboolean print_full = FALSE;
666 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
667 gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
668
669 if (options & pe_print_clone_details) {
670 print_full = TRUE;
671 }
672
673 if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
674
675 if (partially_active
676 || !pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
677 print_full = TRUE;
678 }
679
680
681
682 } else if (pcmk_is_set(options, pe_print_pending)
683 && (child_rsc->pending_task != NULL)
684 && strcmp(child_rsc->pending_task, "probe")) {
685
686 print_full = TRUE;
687
688 } else if (partially_active == FALSE) {
689
690 if (!pcmk_is_set(child_rsc->flags, pcmk_rsc_removed)
691 && !pcmk_is_set(options, pe_print_clone_active)) {
692
693 pcmk__add_word(&stopped_list, 1024, child_rsc->id);
694 }
695
696 } else if (is_set_recursive(child_rsc, pcmk_rsc_removed, TRUE)
697 || !is_set_recursive(child_rsc, pcmk_rsc_managed, FALSE)
698 || is_set_recursive(child_rsc, pcmk_rsc_failed, TRUE)) {
699
700
701 print_full = TRUE;
702
703 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
704
705
706 pcmk_node_t *location = NULL;
707
708 location = child_rsc->fns->location(child_rsc, NULL, TRUE);
709 if (location) {
710
711
712 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
713
714 if (location->details->online == FALSE && location->details->unclean) {
715 print_full = TRUE;
716
717 } else if (a_role > pcmk_role_unpromoted) {
718 promoted_list = g_list_append(promoted_list, location);
719
720 } else {
721 started_list = g_list_append(started_list, location);
722 }
723
724 } else {
725
726 print_full = TRUE;
727 }
728
729 } else {
730
731 print_full = TRUE;
732 }
733
734 if (print_full) {
735 if (options & pe_print_html) {
736 status_print("<li>\n");
737 }
738 child_rsc->fns->print(child_rsc, child_text, options, print_data);
739 if (options & pe_print_html) {
740 status_print("</li>\n");
741 }
742 }
743 }
744
745
746 promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
747 for (gIter = promoted_list; gIter; gIter = gIter->next) {
748 pcmk_node_t *host = gIter->data;
749
750 pcmk__add_word(&list_text, 1024, host->details->uname);
751 active_instances++;
752 }
753
754 if (list_text != NULL) {
755 short_print((const char *) list_text->str, child_text,
756 PROMOTED_INSTANCES, NULL, options, print_data);
757 g_string_truncate(list_text, 0);
758 }
759 g_list_free(promoted_list);
760
761
762 started_list = g_list_sort(started_list, pe__cmp_node_name);
763 for (gIter = started_list; gIter; gIter = gIter->next) {
764 pcmk_node_t *host = gIter->data;
765
766 pcmk__add_word(&list_text, 1024, host->details->uname);
767 active_instances++;
768 }
769
770 if (list_text != NULL) {
771 if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
772 enum rsc_role_e role = configured_role(rsc);
773
774 if (role == pcmk_role_unpromoted) {
775 short_print((const char *) list_text->str, child_text,
776 UNPROMOTED_INSTANCES " (" PCMK_META_TARGET_ROLE ")",
777 NULL, options, print_data);
778 } else {
779 short_print((const char *) list_text->str, child_text,
780 UNPROMOTED_INSTANCES, NULL, options, print_data);
781 }
782
783 } else {
784 short_print((const char *) list_text->str, child_text, "Started",
785 NULL, options, print_data);
786 }
787 }
788
789 g_list_free(started_list);
790
791 if (!pcmk_is_set(options, pe_print_clone_active)) {
792 const char *state = "Stopped";
793 enum rsc_role_e role = configured_role(rsc);
794
795 if (role == pcmk_role_stopped) {
796 state = "Stopped (disabled)";
797 }
798
799 if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
800 && (clone_data->clone_max > active_instances)) {
801
802 GList *nIter;
803 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
804
805
806 if (stopped_list != NULL) {
807 g_string_truncate(stopped_list, 0);
808 }
809
810 if (list == NULL) {
811
812
813
814
815 list = g_hash_table_get_values(rsc->known_on);
816 }
817
818 list = g_list_sort(list, pe__cmp_node_name);
819 for (nIter = list; nIter != NULL; nIter = nIter->next) {
820 pcmk_node_t *node = (pcmk_node_t *) nIter->data;
821
822 if (pcmk__find_node_in_list(rsc->running_on,
823 node->details->uname) == NULL) {
824 pcmk__add_word(&stopped_list, 1024, node->details->uname);
825 }
826 }
827 g_list_free(list);
828 }
829
830 if (stopped_list != NULL) {
831 short_print((const char *) stopped_list->str, child_text, state,
832 NULL, options, print_data);
833 }
834 }
835
836 if (options & pe_print_html) {
837 status_print("</ul>\n");
838 }
839
840 if (list_text != NULL) {
841 g_string_free(list_text, TRUE);
842 }
843
844 if (stopped_list != NULL) {
845 g_string_free(stopped_list, TRUE);
846 }
847 free(child_text);
848 }
849
850 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
851 "GList *")
852 int
853 pe__clone_xml(pcmk__output_t *out, va_list args)
854 {
855 uint32_t show_opts = va_arg(args, uint32_t);
856 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
857 GList *only_node = va_arg(args, GList *);
858 GList *only_rsc = va_arg(args, GList *);
859
860 GList *gIter = rsc->children;
861 GList *all = NULL;
862 int rc = pcmk_rc_no_output;
863 gboolean printed_header = FALSE;
864 gboolean print_everything = TRUE;
865
866 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
867 return rc;
868 }
869
870 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
871 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
872
873 all = g_list_prepend(all, (gpointer) "*");
874
875 for (; gIter != NULL; gIter = gIter->next) {
876 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
877
878 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
879 continue;
880 }
881
882 if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
883 continue;
884 }
885
886 if (!printed_header) {
887 const char *multi_state = pcmk__flag_text(rsc->flags,
888 pcmk_rsc_promotable);
889 const char *unique = pcmk__flag_text(rsc->flags, pcmk_rsc_unique);
890 const char *maintenance = pcmk__flag_text(rsc->flags,
891 pcmk_rsc_maintenance);
892 const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed);
893 const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc));
894 const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed);
895 const char *ignored = pcmk__flag_text(rsc->flags,
896 pcmk_rsc_ignore_failure);
897 const char *target_role = configured_role_str(rsc);
898 const char *desc = pe__resource_description(rsc, show_opts);
899
900 printed_header = TRUE;
901
902 rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_CLONE,
903 PCMK_XA_ID, rsc->id,
904 PCMK_XA_MULTI_STATE, multi_state,
905 PCMK_XA_UNIQUE, unique,
906 PCMK_XA_MAINTENANCE, maintenance,
907 PCMK_XA_MANAGED, managed,
908 PCMK_XA_DISABLED, disabled,
909 PCMK_XA_FAILED, failed,
910 PCMK_XA_FAILURE_IGNORED, ignored,
911 PCMK_XA_TARGET_ROLE, target_role,
912 PCMK_XA_DESCRIPTION, desc,
913 NULL);
914 pcmk__assert(rc == pcmk_rc_ok);
915 }
916
917 out->message(out, (const char *) child_rsc->xml->name, show_opts,
918 child_rsc, only_node, all);
919 }
920
921 if (printed_header) {
922 pcmk__output_xml_pop_parent(out);
923 }
924
925 g_list_free(all);
926 return rc;
927 }
928
929 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
930 "GList *")
931 int
932 pe__clone_default(pcmk__output_t *out, va_list args)
933 {
934 uint32_t show_opts = va_arg(args, uint32_t);
935 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
936 GList *only_node = va_arg(args, GList *);
937 GList *only_rsc = va_arg(args, GList *);
938
939 GHashTable *stopped = NULL;
940
941 GString *list_text = NULL;
942
943 GList *promoted_list = NULL;
944 GList *started_list = NULL;
945 GList *gIter = rsc->children;
946
947 const char *desc = NULL;
948
949 clone_variant_data_t *clone_data = NULL;
950 int active_instances = 0;
951 int rc = pcmk_rc_no_output;
952 gboolean print_everything = TRUE;
953
954 desc = pe__resource_description(rsc, show_opts);
955
956 get_clone_variant_data(clone_data, rsc);
957
958 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
959 return rc;
960 }
961
962 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
963 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
964
965 for (; gIter != NULL; gIter = gIter->next) {
966 gboolean print_full = FALSE;
967 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
968 gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
969
970 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
971 continue;
972 }
973
974 if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
975 continue;
976 }
977
978 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
979 print_full = TRUE;
980 }
981
982 if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
983
984 if (partially_active
985 || !pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
986 print_full = TRUE;
987 }
988
989
990
991 } else if (pcmk_is_set(show_opts, pcmk_show_pending)
992 && (child_rsc->pending_task != NULL)
993 && strcmp(child_rsc->pending_task, "probe")) {
994
995 print_full = TRUE;
996
997 } else if (partially_active == FALSE) {
998
999 if (!pcmk_is_set(child_rsc->flags, pcmk_rsc_removed)
1000 && !pcmk_is_set(show_opts, pcmk_show_clone_detail)
1001 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
1002 if (stopped == NULL) {
1003 stopped = pcmk__strkey_table(free, free);
1004 }
1005 pcmk__insert_dup(stopped, child_rsc->id, "Stopped");
1006 }
1007
1008 } else if (is_set_recursive(child_rsc, pcmk_rsc_removed, TRUE)
1009 || !is_set_recursive(child_rsc, pcmk_rsc_managed, FALSE)
1010 || is_set_recursive(child_rsc, pcmk_rsc_failed, TRUE)) {
1011
1012
1013 print_full = TRUE;
1014
1015 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
1016
1017
1018 pcmk_node_t *location = NULL;
1019
1020 location = child_rsc->fns->location(child_rsc, NULL, TRUE);
1021 if (location) {
1022
1023
1024 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
1025
1026 if (location->details->online == FALSE && location->details->unclean) {
1027 print_full = TRUE;
1028
1029 } else if (a_role > pcmk_role_unpromoted) {
1030 promoted_list = g_list_append(promoted_list, location);
1031
1032 } else {
1033 started_list = g_list_append(started_list, location);
1034 }
1035
1036 } else {
1037
1038 print_full = TRUE;
1039 }
1040
1041 } else {
1042
1043 print_full = TRUE;
1044 }
1045
1046 if (print_full) {
1047 GList *all = NULL;
1048
1049 clone_header(out, &rc, rsc, clone_data, desc);
1050
1051
1052 all = g_list_prepend(all, (gpointer) "*");
1053 out->message(out, (const char *) child_rsc->xml->name, show_opts,
1054 child_rsc, only_node, all);
1055 g_list_free(all);
1056 }
1057 }
1058
1059 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
1060 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1061 return pcmk_rc_ok;
1062 }
1063
1064
1065 promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
1066 for (gIter = promoted_list; gIter; gIter = gIter->next) {
1067 pcmk_node_t *host = gIter->data;
1068
1069 if (!pcmk__str_in_list(host->details->uname, only_node,
1070 pcmk__str_star_matches|pcmk__str_casei)) {
1071 continue;
1072 }
1073
1074 pcmk__add_word(&list_text, 1024, host->details->uname);
1075 active_instances++;
1076 }
1077 g_list_free(promoted_list);
1078
1079 if ((list_text != NULL) && (list_text->len > 0)) {
1080 clone_header(out, &rc, rsc, clone_data, desc);
1081
1082 out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]",
1083 (const char *) list_text->str);
1084 g_string_truncate(list_text, 0);
1085 }
1086
1087
1088 started_list = g_list_sort(started_list, pe__cmp_node_name);
1089 for (gIter = started_list; gIter; gIter = gIter->next) {
1090 pcmk_node_t *host = gIter->data;
1091
1092 if (!pcmk__str_in_list(host->details->uname, only_node,
1093 pcmk__str_star_matches|pcmk__str_casei)) {
1094 continue;
1095 }
1096
1097 pcmk__add_word(&list_text, 1024, host->details->uname);
1098 active_instances++;
1099 }
1100 g_list_free(started_list);
1101
1102 if ((list_text != NULL) && (list_text->len > 0)) {
1103 clone_header(out, &rc, rsc, clone_data, desc);
1104
1105 if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
1106 enum rsc_role_e role = configured_role(rsc);
1107
1108 if (role == pcmk_role_unpromoted) {
1109 out->list_item(out, NULL,
1110 UNPROMOTED_INSTANCES
1111 " (" PCMK_META_TARGET_ROLE "): [ %s ]",
1112 (const char *) list_text->str);
1113 } else {
1114 out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
1115 (const char *) list_text->str);
1116 }
1117
1118 } else {
1119 out->list_item(out, NULL, "Started: [ %s ]",
1120 (const char *) list_text->str);
1121 }
1122 }
1123
1124 if (list_text != NULL) {
1125 g_string_free(list_text, TRUE);
1126 }
1127
1128 if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
1129 if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
1130 && (clone_data->clone_max > active_instances)) {
1131
1132 GList *nIter;
1133 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
1134
1135
1136 if (stopped != NULL) {
1137 g_hash_table_destroy(stopped);
1138 stopped = NULL;
1139 }
1140
1141 if (list == NULL) {
1142
1143
1144
1145
1146 list = g_hash_table_get_values(rsc->known_on);
1147 }
1148
1149 list = g_list_sort(list, pe__cmp_node_name);
1150 for (nIter = list; nIter != NULL; nIter = nIter->next) {
1151 pcmk_node_t *node = (pcmk_node_t *) nIter->data;
1152
1153 if ((pcmk__find_node_in_list(rsc->running_on,
1154 node->details->uname) == NULL)
1155 && pcmk__str_in_list(node->details->uname, only_node,
1156 pcmk__str_star_matches|pcmk__str_casei)) {
1157 xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node->details->uname);
1158 const char *state = "Stopped";
1159
1160 if (configured_role(rsc) == pcmk_role_stopped) {
1161 state = "Stopped (disabled)";
1162 }
1163
1164 if (stopped == NULL) {
1165 stopped = pcmk__strkey_table(free, free);
1166 }
1167 if (probe_op != NULL) {
1168 int rc;
1169
1170 pcmk__scan_min_int(crm_element_value(probe_op,
1171 PCMK__XA_RC_CODE),
1172 &rc, 0);
1173 g_hash_table_insert(stopped, strdup(node->details->uname),
1174 crm_strdup_printf("Stopped (%s)", services_ocf_exitcode_str(rc)));
1175 } else {
1176 pcmk__insert_dup(stopped, node->details->uname, state);
1177 }
1178 }
1179 }
1180 g_list_free(list);
1181 }
1182
1183 if (stopped != NULL) {
1184 GList *list = sorted_hash_table_values(stopped);
1185
1186 clone_header(out, &rc, rsc, clone_data, desc);
1187
1188 for (GList *status_iter = list; status_iter != NULL; status_iter = status_iter->next) {
1189 const char *status = status_iter->data;
1190 GList *nodes = nodes_with_status(stopped, status);
1191 GString *nodes_str = node_list_to_str(nodes);
1192
1193 if (nodes_str != NULL) {
1194 if (nodes_str->len > 0) {
1195 out->list_item(out, NULL, "%s: [ %s ]", status,
1196 (const char *) nodes_str->str);
1197 }
1198 g_string_free(nodes_str, TRUE);
1199 }
1200
1201 g_list_free(nodes);
1202 }
1203
1204 g_list_free(list);
1205 g_hash_table_destroy(stopped);
1206
1207
1208
1209
1210
1211 } else if (active_instances == 0) {
1212 clone_header(out, &rc, rsc, clone_data, desc);
1213 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1214 return rc;
1215 }
1216 }
1217
1218 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1219 return rc;
1220 }
1221
1222 void
1223 clone_free(pcmk_resource_t * rsc)
1224 {
1225 clone_variant_data_t *clone_data = NULL;
1226
1227 get_clone_variant_data(clone_data, rsc);
1228
1229 pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
1230
1231 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1232 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
1233
1234 pcmk__assert(child_rsc);
1235 pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
1236 free_xml(child_rsc->xml);
1237 child_rsc->xml = NULL;
1238
1239 free_xml(child_rsc->orig_xml);
1240 child_rsc->orig_xml = NULL;
1241 child_rsc->fns->free(child_rsc);
1242 }
1243
1244 g_list_free(rsc->children);
1245
1246 if (clone_data) {
1247 pcmk__assert((clone_data->demote_notify == NULL)
1248 && (clone_data->stop_notify == NULL)
1249 && (clone_data->start_notify == NULL)
1250 && (clone_data->promote_notify == NULL));
1251 }
1252
1253 common_free(rsc);
1254 }
1255
1256 enum rsc_role_e
1257 clone_resource_state(const pcmk_resource_t * rsc, gboolean current)
1258 {
1259 enum rsc_role_e clone_role = pcmk_role_unknown;
1260 GList *gIter = rsc->children;
1261
1262 for (; gIter != NULL; gIter = gIter->next) {
1263 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
1264 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
1265
1266 if (a_role > clone_role) {
1267 clone_role = a_role;
1268 }
1269 }
1270
1271 pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(clone_role));
1272 return clone_role;
1273 }
1274
1275
1276
1277
1278
1279
1280
1281
1282 bool
1283 pe__is_universal_clone(const pcmk_resource_t *rsc,
1284 const pcmk_scheduler_t *scheduler)
1285 {
1286 if (pcmk__is_clone(rsc)) {
1287 clone_variant_data_t *clone_data = rsc->variant_opaque;
1288
1289 if (clone_data->clone_max == g_list_length(scheduler->nodes)) {
1290 return TRUE;
1291 }
1292 }
1293 return FALSE;
1294 }
1295
1296 gboolean
1297 pe__clone_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
1298 gboolean check_parent)
1299 {
1300 gboolean passes = FALSE;
1301 clone_variant_data_t *clone_data = NULL;
1302
1303 if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
1304 passes = TRUE;
1305 } else {
1306 get_clone_variant_data(clone_data, rsc);
1307 passes = pcmk__str_in_list(pcmk__xe_id(clone_data->xml_obj_child),
1308 only_rsc, pcmk__str_star_matches);
1309
1310 if (!passes) {
1311 for (const GList *iter = rsc->children;
1312 iter != NULL; iter = iter->next) {
1313
1314 const pcmk_resource_t *child_rsc = NULL;
1315
1316 child_rsc = (const pcmk_resource_t *) iter->data;
1317 if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
1318 passes = TRUE;
1319 break;
1320 }
1321 }
1322 }
1323 }
1324 return !passes;
1325 }
1326
1327 const char *
1328 pe__clone_child_id(const pcmk_resource_t *rsc)
1329 {
1330 clone_variant_data_t *clone_data = NULL;
1331 get_clone_variant_data(clone_data, rsc);
1332 return pcmk__xe_id(clone_data->xml_obj_child);
1333 }
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343 bool
1344 pe__clone_is_ordered(const pcmk_resource_t *clone)
1345 {
1346 clone_variant_data_t *clone_data = NULL;
1347
1348 get_clone_variant_data(clone_data, clone);
1349 return pcmk_is_set(clone_data->flags, pcmk__clone_ordered);
1350 }
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362 int
1363 pe__set_clone_flag(pcmk_resource_t *clone, enum pcmk__clone_flags flag)
1364 {
1365 clone_variant_data_t *clone_data = NULL;
1366
1367 get_clone_variant_data(clone_data, clone);
1368 if (pcmk_is_set(clone_data->flags, flag)) {
1369 return pcmk_rc_already;
1370 }
1371 clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
1372 "Clone", clone->id,
1373 clone_data->flags, flag, "flag");
1374 return pcmk_rc_ok;
1375 }
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386 bool
1387 pe__clone_flag_is_set(const pcmk_resource_t *clone, uint32_t flags)
1388 {
1389 clone_variant_data_t *clone_data = NULL;
1390
1391 get_clone_variant_data(clone_data, clone);
1392 pcmk__assert(clone_data != NULL);
1393
1394 return pcmk_all_flags_set(clone_data->flags, flags);
1395 }
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405 void
1406 pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting,
1407 bool any_demoting)
1408 {
1409 pcmk_action_t *action = NULL;
1410 pcmk_action_t *action_complete = NULL;
1411 clone_variant_data_t *clone_data = NULL;
1412
1413 get_clone_variant_data(clone_data, clone);
1414
1415
1416 action = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTE,
1417 !any_promoting, true);
1418
1419
1420 action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTED,
1421 !any_promoting, true);
1422 action_complete->priority = PCMK_SCORE_INFINITY;
1423
1424
1425 if (clone_data->promote_notify == NULL) {
1426 clone_data->promote_notify = pe__action_notif_pseudo_ops(clone,
1427 PCMK_ACTION_PROMOTE,
1428 action,
1429 action_complete);
1430 }
1431
1432
1433 action = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTE,
1434 !any_demoting, true);
1435
1436
1437 action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTED,
1438 !any_demoting, true);
1439 action_complete->priority = PCMK_SCORE_INFINITY;
1440
1441
1442 if (clone_data->demote_notify == NULL) {
1443 clone_data->demote_notify = pe__action_notif_pseudo_ops(clone,
1444 PCMK_ACTION_DEMOTE,
1445 action,
1446 action_complete);
1447
1448 if (clone_data->promote_notify != NULL) {
1449 order_actions(clone_data->stop_notify->post_done,
1450 clone_data->promote_notify->pre, pcmk__ar_ordered);
1451 order_actions(clone_data->start_notify->post_done,
1452 clone_data->promote_notify->pre, pcmk__ar_ordered);
1453 order_actions(clone_data->demote_notify->post_done,
1454 clone_data->promote_notify->pre, pcmk__ar_ordered);
1455 order_actions(clone_data->demote_notify->post_done,
1456 clone_data->start_notify->pre, pcmk__ar_ordered);
1457 order_actions(clone_data->demote_notify->post_done,
1458 clone_data->stop_notify->pre, pcmk__ar_ordered);
1459 }
1460 }
1461 }
1462
1463
1464
1465
1466
1467
1468
1469 void
1470 pe__create_clone_notifications(pcmk_resource_t *clone)
1471 {
1472 clone_variant_data_t *clone_data = NULL;
1473
1474 get_clone_variant_data(clone_data, clone);
1475
1476 pe__create_action_notifications(clone, clone_data->start_notify);
1477 pe__create_action_notifications(clone, clone_data->stop_notify);
1478 pe__create_action_notifications(clone, clone_data->promote_notify);
1479 pe__create_action_notifications(clone, clone_data->demote_notify);
1480 }
1481
1482
1483
1484
1485
1486
1487
1488 void
1489 pe__free_clone_notification_data(pcmk_resource_t *clone)
1490 {
1491 clone_variant_data_t *clone_data = NULL;
1492
1493 get_clone_variant_data(clone_data, clone);
1494
1495 pe__free_action_notification_data(clone_data->demote_notify);
1496 clone_data->demote_notify = NULL;
1497
1498 pe__free_action_notification_data(clone_data->stop_notify);
1499 clone_data->stop_notify = NULL;
1500
1501 pe__free_action_notification_data(clone_data->start_notify);
1502 clone_data->start_notify = NULL;
1503
1504 pe__free_action_notification_data(clone_data->promote_notify);
1505 clone_data->promote_notify = NULL;
1506 }
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518 void
1519 pe__create_clone_notif_pseudo_ops(pcmk_resource_t *clone,
1520 pcmk_action_t *start, pcmk_action_t *started,
1521 pcmk_action_t *stop, pcmk_action_t *stopped)
1522 {
1523 clone_variant_data_t *clone_data = NULL;
1524
1525 get_clone_variant_data(clone_data, clone);
1526
1527 if (clone_data->start_notify == NULL) {
1528 clone_data->start_notify = pe__action_notif_pseudo_ops(clone,
1529 PCMK_ACTION_START,
1530 start, started);
1531 }
1532
1533 if (clone_data->stop_notify == NULL) {
1534 clone_data->stop_notify = pe__action_notif_pseudo_ops(clone,
1535 PCMK_ACTION_STOP,
1536 stop, stopped);
1537 if ((clone_data->start_notify != NULL)
1538 && (clone_data->stop_notify != NULL)) {
1539 order_actions(clone_data->stop_notify->post_done,
1540 clone_data->start_notify->pre, pcmk__ar_ordered);
1541 }
1542 }
1543 }
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553 unsigned int
1554 pe__clone_max_per_node(const pcmk_resource_t *rsc)
1555 {
1556 const clone_variant_data_t *clone_data = NULL;
1557
1558 get_clone_variant_data(clone_data, rsc);
1559 return clone_data->clone_node_max;
1560 }