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