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
- 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__create_promotable_pseudo_ops
- pe__create_clone_notifications
- pe__free_clone_notification_data
- pe__create_clone_notif_pseudo_ops
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
22 #ifdef PCMK__COMPAT_2_0
23 #define PROMOTED_INSTANCES RSC_ROLE_PROMOTED_LEGACY_S "s"
24 #define UNPROMOTED_INSTANCES RSC_ROLE_UNPROMOTED_LEGACY_S "s"
25 #else
26 #define PROMOTED_INSTANCES RSC_ROLE_PROMOTED_S
27 #define UNPROMOTED_INSTANCES RSC_ROLE_UNPROMOTED_S
28 #endif
29
30 typedef struct clone_variant_data_s {
31 int clone_max;
32 int clone_node_max;
33
34 int promoted_max;
35 int promoted_node_max;
36
37 int total_clones;
38
39 uint32_t flags;
40
41 notify_data_t *stop_notify;
42 notify_data_t *start_notify;
43 notify_data_t *demote_notify;
44 notify_data_t *promote_notify;
45
46 xmlNode *xml_obj_child;
47 } clone_variant_data_t;
48
49 #define get_clone_variant_data(data, rsc) \
50 CRM_ASSERT((rsc != NULL) && (rsc->variant == pe_clone)); \
51 data = (clone_variant_data_t *) rsc->variant_opaque;
52
53
54
55
56
57
58
59
60
61 int
62 pe__clone_max(const pe_resource_t *clone)
63 {
64 const clone_variant_data_t *clone_data = NULL;
65
66 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
67 return clone_data->clone_max;
68 }
69
70
71
72
73
74
75
76
77
78 int
79 pe__clone_node_max(const pe_resource_t *clone)
80 {
81 const clone_variant_data_t *clone_data = NULL;
82
83 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
84 return clone_data->clone_node_max;
85 }
86
87
88
89
90
91
92
93
94
95 int
96 pe__clone_promoted_max(const pe_resource_t *clone)
97 {
98 clone_variant_data_t *clone_data = NULL;
99
100 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
101 return clone_data->promoted_max;
102 }
103
104
105
106
107
108
109
110
111
112 int
113 pe__clone_promoted_node_max(const pe_resource_t *clone)
114 {
115 clone_variant_data_t *clone_data = NULL;
116
117 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
118 return clone_data->promoted_node_max;
119 }
120
121 static GList *
122 sorted_hash_table_values(GHashTable *table)
123 {
124 GList *retval = NULL;
125 GHashTableIter iter;
126 gpointer key, value;
127
128 g_hash_table_iter_init(&iter, table);
129 while (g_hash_table_iter_next(&iter, &key, &value)) {
130 if (!g_list_find_custom(retval, value, (GCompareFunc) strcmp)) {
131 retval = g_list_prepend(retval, (char *) value);
132 }
133 }
134
135 retval = g_list_sort(retval, (GCompareFunc) strcmp);
136 return retval;
137 }
138
139 static GList *
140 nodes_with_status(GHashTable *table, const char *status)
141 {
142 GList *retval = NULL;
143 GHashTableIter iter;
144 gpointer key, value;
145
146 g_hash_table_iter_init(&iter, table);
147 while (g_hash_table_iter_next(&iter, &key, &value)) {
148 if (!strcmp((char *) value, status)) {
149 retval = g_list_prepend(retval, key);
150 }
151 }
152
153 retval = g_list_sort(retval, (GCompareFunc) pcmk__numeric_strcasecmp);
154 return retval;
155 }
156
157 static GString *
158 node_list_to_str(const GList *list)
159 {
160 GString *retval = NULL;
161
162 for (const GList *iter = list; iter != NULL; iter = iter->next) {
163 pcmk__add_word(&retval, 1024, (const char *) iter->data);
164 }
165
166 return retval;
167 }
168
169 static void
170 clone_header(pcmk__output_t *out, int *rc, const pe_resource_t *rsc,
171 clone_variant_data_t *clone_data, const char *desc)
172 {
173 GString *attrs = NULL;
174
175 if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
176 pcmk__add_separated_word(&attrs, 64, "promotable", ", ");
177 }
178
179 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
180 pcmk__add_separated_word(&attrs, 64, "unique", ", ");
181 }
182
183 if (pe__resource_is_disabled(rsc)) {
184 pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
185 }
186
187 if (pcmk_is_set(rsc->flags, pe_rsc_maintenance)) {
188 pcmk__add_separated_word(&attrs, 64, "maintenance", ", ");
189
190 } else if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
191 pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
192 }
193
194 if (attrs != NULL) {
195 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)%s%s%s",
196 rsc->id, ID(clone_data->xml_obj_child),
197 (const char *) attrs->str, desc ? " (" : "",
198 desc ? desc : "", desc ? ")" : "");
199 g_string_free(attrs, TRUE);
200 } else {
201 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]%s%s%s",
202 rsc->id, ID(clone_data->xml_obj_child),
203 desc ? " (" : "", desc ? desc : "",
204 desc ? ")" : "");
205 }
206 }
207
208 void
209 pe__force_anon(const char *standard, pe_resource_t *rsc, const char *rid,
210 pe_working_set_t *data_set)
211 {
212 if (pe_rsc_is_clone(rsc)) {
213 clone_variant_data_t *clone_data = rsc->variant_opaque;
214
215 pe_warn("Ignoring " XML_RSC_ATTR_UNIQUE " for %s because %s resources "
216 "such as %s can be used only as anonymous clones",
217 rsc->id, standard, rid);
218
219 clone_data->clone_node_max = 1;
220 clone_data->clone_max = QB_MIN(clone_data->clone_max,
221 g_list_length(data_set->nodes));
222 }
223 }
224
225 pe_resource_t *
226 find_clone_instance(const pe_resource_t *rsc, const char *sub_id)
227 {
228 char *child_id = NULL;
229 pe_resource_t *child = NULL;
230 const char *child_base = NULL;
231 clone_variant_data_t *clone_data = NULL;
232
233 get_clone_variant_data(clone_data, rsc);
234
235 child_base = ID(clone_data->xml_obj_child);
236 child_id = crm_strdup_printf("%s:%s", child_base, sub_id);
237 child = pe_find_resource(rsc->children, child_id);
238
239 free(child_id);
240 return child;
241 }
242
243 pe_resource_t *
244 pe__create_clone_child(pe_resource_t *rsc, pe_working_set_t *data_set)
245 {
246 gboolean as_orphan = FALSE;
247 char *inc_num = NULL;
248 char *inc_max = NULL;
249 pe_resource_t *child_rsc = NULL;
250 xmlNode *child_copy = NULL;
251 clone_variant_data_t *clone_data = NULL;
252
253 get_clone_variant_data(clone_data, rsc);
254
255 CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
256
257 if (clone_data->total_clones >= clone_data->clone_max) {
258
259 as_orphan = TRUE;
260 }
261
262
263 inc_num = pcmk__itoa(clone_data->total_clones);
264 inc_max = pcmk__itoa(clone_data->clone_max);
265
266 child_copy = copy_xml(clone_data->xml_obj_child);
267
268 crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num);
269
270 if (pe__unpack_resource(child_copy, &child_rsc, rsc,
271 data_set) != pcmk_rc_ok) {
272 goto bail;
273 }
274
275
276 CRM_ASSERT(child_rsc);
277 clone_data->total_clones += 1;
278 pe_rsc_trace(child_rsc, "Setting clone attributes for: %s", child_rsc->id);
279 rsc->children = g_list_append(rsc->children, child_rsc);
280 if (as_orphan) {
281 pe__set_resource_flags_recursive(child_rsc, pe_rsc_orphan);
282 }
283
284 add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max);
285 pe_rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
286
287 bail:
288 free(inc_num);
289 free(inc_max);
290
291 return child_rsc;
292 }
293
294 gboolean
295 clone_unpack(pe_resource_t * rsc, pe_working_set_t * data_set)
296 {
297 int lpc = 0;
298 xmlNode *a_child = NULL;
299 xmlNode *xml_obj = rsc->xml;
300 clone_variant_data_t *clone_data = NULL;
301
302 const char *max_clones = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_MAX);
303 const char *max_clones_node = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX);
304
305 pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
306
307 clone_data = calloc(1, sizeof(clone_variant_data_t));
308 rsc->variant_opaque = clone_data;
309
310 if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
311 const char *promoted_max = NULL;
312 const char *promoted_node_max = NULL;
313
314 promoted_max = g_hash_table_lookup(rsc->meta,
315 XML_RSC_ATTR_PROMOTED_MAX);
316 if (promoted_max == NULL) {
317
318 promoted_max = g_hash_table_lookup(rsc->meta,
319 PCMK_XA_PROMOTED_MAX_LEGACY);
320 }
321
322 promoted_node_max = g_hash_table_lookup(rsc->meta,
323 XML_RSC_ATTR_PROMOTED_NODEMAX);
324 if (promoted_node_max == NULL) {
325
326 promoted_node_max =
327 g_hash_table_lookup(rsc->meta,
328 PCMK_XA_PROMOTED_NODE_MAX_LEGACY);
329 }
330
331
332 if (promoted_max == NULL) {
333 clone_data->promoted_max = 1;
334 } else {
335 pcmk__scan_min_int(promoted_max, &(clone_data->promoted_max), 0);
336 }
337
338
339 if (promoted_node_max == NULL) {
340 clone_data->promoted_node_max = 1;
341 } else {
342 pcmk__scan_min_int(promoted_node_max,
343 &(clone_data->promoted_node_max), 0);
344 }
345 }
346
347
348
349
350
351 if (max_clones_node == NULL) {
352 clone_data->clone_node_max = 1;
353 } else {
354 pcmk__scan_min_int(max_clones_node, &(clone_data->clone_node_max), 0);
355 }
356
357
358
359
360 if (max_clones == NULL) {
361 clone_data->clone_max = QB_MAX(1, g_list_length(data_set->nodes));
362 } else {
363 pcmk__scan_min_int(max_clones, &(clone_data->clone_max), 0);
364 }
365
366 if (crm_is_true(g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_ORDERED))) {
367 clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
368 "Clone", rsc->id,
369 clone_data->flags,
370 pe__clone_ordered,
371 "pe__clone_ordered");
372 }
373
374 if ((rsc->flags & pe_rsc_unique) == 0 && clone_data->clone_node_max > 1) {
375 pcmk__config_err("Ignoring " XML_RSC_ATTR_PROMOTED_MAX " for %s "
376 "because anonymous clones support only one instance "
377 "per node", 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, pe_rsc_unique));
386 pe_rsc_trace(rsc, "\tClone is promotable: %s",
387 pe__rsc_bool_str(rsc, pe_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, pe_rsc_unique));
419
420 if (clone_data->clone_max <= 0) {
421
422
423
424 if (pe__create_clone_child(rsc, data_set) == 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, data_set) == 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(pe_resource_t * rsc, gboolean all)
443 {
444 GList *gIter = rsc->children;
445
446 for (; gIter != NULL; gIter = gIter->next) {
447 pe_resource_t *child_rsc = (pe_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(pe_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 target_role = g_hash_table_lookup(((pe_resource_t*)rsc->children->data)->meta,
502 XML_RSC_ATTR_TARGET_ROLE);
503 }
504 return target_role;
505 }
506
507 static enum rsc_role_e
508 configured_role(pe_resource_t * rsc)
509 {
510 const char *target_role = configured_role_str(rsc);
511
512 if (target_role) {
513 return text2role(target_role);
514 }
515 return RSC_ROLE_UNKNOWN;
516 }
517
518
519
520
521
522 static void
523 clone_print_xml(pe_resource_t *rsc, const char *pre_text, long options,
524 void *print_data)
525 {
526 char *child_text = crm_strdup_printf("%s ", pre_text);
527 const char *target_role = configured_role_str(rsc);
528 GList *gIter = rsc->children;
529
530 status_print("%s<clone ", pre_text);
531 status_print(XML_ATTR_ID "=\"%s\" ", rsc->id);
532 status_print("multi_state=\"%s\" ",
533 pe__rsc_bool_str(rsc, pe_rsc_promotable));
534 status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_unique));
535 status_print("managed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_managed));
536 status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_failed));
537 status_print("failure_ignored=\"%s\" ",
538 pe__rsc_bool_str(rsc, pe_rsc_failure_ignored));
539 if (target_role) {
540 status_print("target_role=\"%s\" ", target_role);
541 }
542 status_print(">\n");
543
544 for (; gIter != NULL; gIter = gIter->next) {
545 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
546
547 child_rsc->fns->print(child_rsc, child_text, options, print_data);
548 }
549
550 status_print("%s</clone>\n", pre_text);
551 free(child_text);
552 }
553
554 bool
555 is_set_recursive(const pe_resource_t *rsc, long long flag, bool any)
556 {
557 GList *gIter;
558 bool all = !any;
559
560 if (pcmk_is_set(rsc->flags, flag)) {
561 if(any) {
562 return TRUE;
563 }
564 } else if(all) {
565 return FALSE;
566 }
567
568 for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
569 if(is_set_recursive(gIter->data, flag, any)) {
570 if(any) {
571 return TRUE;
572 }
573
574 } else if(all) {
575 return FALSE;
576 }
577 }
578
579 if(all) {
580 return TRUE;
581 }
582 return FALSE;
583 }
584
585
586
587
588
589 void
590 clone_print(pe_resource_t *rsc, const char *pre_text, long options,
591 void *print_data)
592 {
593 GString *list_text = NULL;
594 char *child_text = NULL;
595 GString *stopped_list = NULL;
596
597 GList *promoted_list = NULL;
598 GList *started_list = NULL;
599 GList *gIter = rsc->children;
600
601 clone_variant_data_t *clone_data = NULL;
602 int active_instances = 0;
603
604 if (pre_text == NULL) {
605 pre_text = " ";
606 }
607
608 if (options & pe_print_xml) {
609 clone_print_xml(rsc, pre_text, options, print_data);
610 return;
611 }
612
613 get_clone_variant_data(clone_data, rsc);
614
615 child_text = crm_strdup_printf("%s ", pre_text);
616
617 status_print("%sClone Set: %s [%s]%s%s%s",
618 pre_text ? pre_text : "", rsc->id, ID(clone_data->xml_obj_child),
619 pcmk_is_set(rsc->flags, pe_rsc_promotable)? " (promotable)" : "",
620 pcmk_is_set(rsc->flags, pe_rsc_unique)? " (unique)" : "",
621 pcmk_is_set(rsc->flags, pe_rsc_managed)? "" : " (unmanaged)");
622
623 if (options & pe_print_html) {
624 status_print("\n<ul>\n");
625
626 } else if ((options & pe_print_log) == 0) {
627 status_print("\n");
628 }
629
630 for (; gIter != NULL; gIter = gIter->next) {
631 gboolean print_full = FALSE;
632 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
633 gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
634
635 if (options & pe_print_clone_details) {
636 print_full = TRUE;
637 }
638
639 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
640
641 if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
642 print_full = TRUE;
643 }
644
645
646
647 } else if (pcmk_is_set(options, pe_print_pending)
648 && (child_rsc->pending_task != NULL)
649 && strcmp(child_rsc->pending_task, "probe")) {
650
651 print_full = TRUE;
652
653 } else if (partially_active == FALSE) {
654
655 if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
656 && !pcmk_is_set(options, pe_print_clone_active)) {
657
658 pcmk__add_word(&stopped_list, 1024, child_rsc->id);
659 }
660
661 } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
662 || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
663 || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
664
665
666 print_full = TRUE;
667
668 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
669
670
671 pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
672
673 if (location) {
674
675
676 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
677
678 if (location->details->online == FALSE && location->details->unclean) {
679 print_full = TRUE;
680
681 } else if (a_role > RSC_ROLE_UNPROMOTED) {
682 promoted_list = g_list_append(promoted_list, location);
683
684 } else {
685 started_list = g_list_append(started_list, location);
686 }
687
688 } else {
689
690 print_full = TRUE;
691 }
692
693 } else {
694
695 print_full = TRUE;
696 }
697
698 if (print_full) {
699 if (options & pe_print_html) {
700 status_print("<li>\n");
701 }
702 child_rsc->fns->print(child_rsc, child_text, options, print_data);
703 if (options & pe_print_html) {
704 status_print("</li>\n");
705 }
706 }
707 }
708
709
710 promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
711 for (gIter = promoted_list; gIter; gIter = gIter->next) {
712 pe_node_t *host = gIter->data;
713
714 pcmk__add_word(&list_text, 1024, host->details->uname);
715 active_instances++;
716 }
717
718 if (list_text != NULL) {
719 short_print((const char *) list_text->str, child_text,
720 PROMOTED_INSTANCES, NULL, options, print_data);
721 g_string_truncate(list_text, 0);
722 }
723 g_list_free(promoted_list);
724
725
726 started_list = g_list_sort(started_list, pe__cmp_node_name);
727 for (gIter = started_list; gIter; gIter = gIter->next) {
728 pe_node_t *host = gIter->data;
729
730 pcmk__add_word(&list_text, 1024, host->details->uname);
731 active_instances++;
732 }
733
734 if (list_text != NULL) {
735 if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
736 enum rsc_role_e role = configured_role(rsc);
737
738 if (role == RSC_ROLE_UNPROMOTED) {
739 short_print((const char *) list_text->str, child_text,
740 UNPROMOTED_INSTANCES " (target-role)", NULL,
741 options, print_data);
742 } else {
743 short_print((const char *) list_text->str, child_text,
744 UNPROMOTED_INSTANCES, NULL, options, print_data);
745 }
746
747 } else {
748 short_print((const char *) list_text->str, child_text, "Started",
749 NULL, options, print_data);
750 }
751 }
752
753 g_list_free(started_list);
754
755 if (!pcmk_is_set(options, pe_print_clone_active)) {
756 const char *state = "Stopped";
757 enum rsc_role_e role = configured_role(rsc);
758
759 if (role == RSC_ROLE_STOPPED) {
760 state = "Stopped (disabled)";
761 }
762
763 if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
764 && (clone_data->clone_max > active_instances)) {
765
766 GList *nIter;
767 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
768
769
770 if (stopped_list != NULL) {
771 g_string_truncate(stopped_list, 0);
772 }
773
774 if (list == NULL) {
775
776
777
778 list = g_hash_table_get_values(rsc->known_on);
779 }
780
781 list = g_list_sort(list, pe__cmp_node_name);
782 for (nIter = list; nIter != NULL; nIter = nIter->next) {
783 pe_node_t *node = (pe_node_t *)nIter->data;
784
785 if (pe_find_node(rsc->running_on, node->details->uname) == NULL) {
786 pcmk__add_word(&stopped_list, 1024, node->details->uname);
787 }
788 }
789 g_list_free(list);
790 }
791
792 if (stopped_list != NULL) {
793 short_print((const char *) stopped_list->str, child_text, state,
794 NULL, options, print_data);
795 }
796 }
797
798 if (options & pe_print_html) {
799 status_print("</ul>\n");
800 }
801
802 if (list_text != NULL) {
803 g_string_free(list_text, TRUE);
804 }
805
806 if (stopped_list != NULL) {
807 g_string_free(stopped_list, TRUE);
808 }
809 free(child_text);
810 }
811
812 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pe_resource_t *", "GList *", "GList *")
813 int
814 pe__clone_xml(pcmk__output_t *out, va_list args)
815 {
816 uint32_t show_opts = va_arg(args, uint32_t);
817 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
818 GList *only_node = va_arg(args, GList *);
819 GList *only_rsc = va_arg(args, GList *);
820
821
822 const char *desc = NULL;
823 GList *gIter = rsc->children;
824 GList *all = NULL;
825 int rc = pcmk_rc_no_output;
826 gboolean printed_header = FALSE;
827 gboolean print_everything = TRUE;
828
829
830
831 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
832 return rc;
833 }
834
835 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
836 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
837
838 all = g_list_prepend(all, (gpointer) "*");
839
840 for (; gIter != NULL; gIter = gIter->next) {
841 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
842
843 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
844 continue;
845 }
846
847 if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
848 continue;
849 }
850
851 if (!printed_header) {
852 printed_header = TRUE;
853
854 desc = pe__resource_description(rsc, show_opts);
855
856 rc = pe__name_and_nvpairs_xml(out, true, "clone", 10,
857 "id", rsc->id,
858 "multi_state", pe__rsc_bool_str(rsc, pe_rsc_promotable),
859 "unique", pe__rsc_bool_str(rsc, pe_rsc_unique),
860 "maintenance", pe__rsc_bool_str(rsc, pe_rsc_maintenance),
861 "managed", pe__rsc_bool_str(rsc, pe_rsc_managed),
862 "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)),
863 "failed", pe__rsc_bool_str(rsc, pe_rsc_failed),
864 "failure_ignored", pe__rsc_bool_str(rsc, pe_rsc_failure_ignored),
865 "target_role", configured_role_str(rsc),
866 "description", desc);
867 CRM_ASSERT(rc == pcmk_rc_ok);
868 }
869
870 out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
871 child_rsc, only_node, all);
872 }
873
874 if (printed_header) {
875 pcmk__output_xml_pop_parent(out);
876 }
877
878 g_list_free(all);
879 return rc;
880 }
881
882 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pe_resource_t *", "GList *", "GList *")
883 int
884 pe__clone_default(pcmk__output_t *out, va_list args)
885 {
886 uint32_t show_opts = va_arg(args, uint32_t);
887 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
888 GList *only_node = va_arg(args, GList *);
889 GList *only_rsc = va_arg(args, GList *);
890
891 GHashTable *stopped = NULL;
892
893 GString *list_text = NULL;
894
895 GList *promoted_list = NULL;
896 GList *started_list = NULL;
897 GList *gIter = rsc->children;
898
899 const char *desc = NULL;
900
901 clone_variant_data_t *clone_data = NULL;
902 int active_instances = 0;
903 int rc = pcmk_rc_no_output;
904 gboolean print_everything = TRUE;
905
906 desc = pe__resource_description(rsc, show_opts);
907
908 get_clone_variant_data(clone_data, rsc);
909
910 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
911 return rc;
912 }
913
914 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
915 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
916
917 for (; gIter != NULL; gIter = gIter->next) {
918 gboolean print_full = FALSE;
919 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
920 gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
921
922 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
923 continue;
924 }
925
926 if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
927 continue;
928 }
929
930 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
931 print_full = TRUE;
932 }
933
934 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
935
936 if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
937 print_full = TRUE;
938 }
939
940
941
942 } else if (pcmk_is_set(show_opts, pcmk_show_pending)
943 && (child_rsc->pending_task != NULL)
944 && strcmp(child_rsc->pending_task, "probe")) {
945
946 print_full = TRUE;
947
948 } else if (partially_active == FALSE) {
949
950 if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
951 && !pcmk_is_set(show_opts, pcmk_show_clone_detail)
952 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
953 if (stopped == NULL) {
954 stopped = pcmk__strkey_table(free, free);
955 }
956 g_hash_table_insert(stopped, strdup(child_rsc->id), strdup("Stopped"));
957 }
958
959 } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
960 || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
961 || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
962
963
964 print_full = TRUE;
965
966 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
967
968
969 pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
970
971 if (location) {
972
973
974 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
975
976 if (location->details->online == FALSE && location->details->unclean) {
977 print_full = TRUE;
978
979 } else if (a_role > RSC_ROLE_UNPROMOTED) {
980 promoted_list = g_list_append(promoted_list, location);
981
982 } else {
983 started_list = g_list_append(started_list, location);
984 }
985
986 } else {
987
988 print_full = TRUE;
989 }
990
991 } else {
992
993 print_full = TRUE;
994 }
995
996 if (print_full) {
997 GList *all = NULL;
998
999 clone_header(out, &rc, rsc, clone_data, desc);
1000
1001
1002 all = g_list_prepend(all, (gpointer) "*");
1003 out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
1004 child_rsc, only_node, all);
1005 g_list_free(all);
1006 }
1007 }
1008
1009 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
1010 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1011 return pcmk_rc_ok;
1012 }
1013
1014
1015 promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
1016 for (gIter = promoted_list; gIter; gIter = gIter->next) {
1017 pe_node_t *host = gIter->data;
1018
1019 if (!pcmk__str_in_list(host->details->uname, only_node,
1020 pcmk__str_star_matches|pcmk__str_casei)) {
1021 continue;
1022 }
1023
1024 pcmk__add_word(&list_text, 1024, host->details->uname);
1025 active_instances++;
1026 }
1027 g_list_free(promoted_list);
1028
1029 if ((list_text != NULL) && (list_text->len > 0)) {
1030 clone_header(out, &rc, rsc, clone_data, desc);
1031
1032 out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]",
1033 (const char *) list_text->str);
1034 g_string_truncate(list_text, 0);
1035 }
1036
1037
1038 started_list = g_list_sort(started_list, pe__cmp_node_name);
1039 for (gIter = started_list; gIter; gIter = gIter->next) {
1040 pe_node_t *host = gIter->data;
1041
1042 if (!pcmk__str_in_list(host->details->uname, only_node,
1043 pcmk__str_star_matches|pcmk__str_casei)) {
1044 continue;
1045 }
1046
1047 pcmk__add_word(&list_text, 1024, host->details->uname);
1048 active_instances++;
1049 }
1050 g_list_free(started_list);
1051
1052 if ((list_text != NULL) && (list_text->len > 0)) {
1053 clone_header(out, &rc, rsc, clone_data, desc);
1054
1055 if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
1056 enum rsc_role_e role = configured_role(rsc);
1057
1058 if (role == RSC_ROLE_UNPROMOTED) {
1059 out->list_item(out, NULL,
1060 UNPROMOTED_INSTANCES " (target-role): [ %s ]",
1061 (const char *) list_text->str);
1062 } else {
1063 out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
1064 (const char *) list_text->str);
1065 }
1066
1067 } else {
1068 out->list_item(out, NULL, "Started: [ %s ]",
1069 (const char *) list_text->str);
1070 }
1071 }
1072
1073 if (list_text != NULL) {
1074 g_string_free(list_text, TRUE);
1075 }
1076
1077 if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
1078 if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
1079 && (clone_data->clone_max > active_instances)) {
1080
1081 GList *nIter;
1082 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
1083
1084
1085 if (stopped != NULL) {
1086 g_hash_table_destroy(stopped);
1087 stopped = NULL;
1088 }
1089
1090 if (list == NULL) {
1091
1092
1093
1094 list = g_hash_table_get_values(rsc->known_on);
1095 }
1096
1097 list = g_list_sort(list, pe__cmp_node_name);
1098 for (nIter = list; nIter != NULL; nIter = nIter->next) {
1099 pe_node_t *node = (pe_node_t *)nIter->data;
1100
1101 if (pe_find_node(rsc->running_on, node->details->uname) == NULL &&
1102 pcmk__str_in_list(node->details->uname, only_node,
1103 pcmk__str_star_matches|pcmk__str_casei)) {
1104 xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node->details->uname);
1105 const char *state = "Stopped";
1106
1107 if (configured_role(rsc) == RSC_ROLE_STOPPED) {
1108 state = "Stopped (disabled)";
1109 }
1110
1111 if (stopped == NULL) {
1112 stopped = pcmk__strkey_table(free, free);
1113 }
1114 if (probe_op != NULL) {
1115 int rc;
1116
1117 pcmk__scan_min_int(crm_element_value(probe_op, XML_LRM_ATTR_RC), &rc, 0);
1118 g_hash_table_insert(stopped, strdup(node->details->uname),
1119 crm_strdup_printf("Stopped (%s)", services_ocf_exitcode_str(rc)));
1120 } else {
1121 g_hash_table_insert(stopped, strdup(node->details->uname),
1122 strdup(state));
1123 }
1124 }
1125 }
1126 g_list_free(list);
1127 }
1128
1129 if (stopped != NULL) {
1130 GList *list = sorted_hash_table_values(stopped);
1131
1132 clone_header(out, &rc, rsc, clone_data, desc);
1133
1134 for (GList *status_iter = list; status_iter != NULL; status_iter = status_iter->next) {
1135 const char *status = status_iter->data;
1136 GList *nodes = nodes_with_status(stopped, status);
1137 GString *nodes_str = node_list_to_str(nodes);
1138
1139 if (nodes_str != NULL) {
1140 if (nodes_str->len > 0) {
1141 out->list_item(out, NULL, "%s: [ %s ]", status,
1142 (const char *) nodes_str->str);
1143 }
1144 g_string_free(nodes_str, TRUE);
1145 }
1146
1147 g_list_free(nodes);
1148 }
1149
1150 g_list_free(list);
1151 g_hash_table_destroy(stopped);
1152
1153
1154
1155
1156
1157 } else if (active_instances == 0) {
1158 clone_header(out, &rc, rsc, clone_data, desc);
1159 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1160 return rc;
1161 }
1162 }
1163
1164 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1165 return rc;
1166 }
1167
1168 void
1169 clone_free(pe_resource_t * rsc)
1170 {
1171 clone_variant_data_t *clone_data = NULL;
1172
1173 get_clone_variant_data(clone_data, rsc);
1174
1175 pe_rsc_trace(rsc, "Freeing %s", rsc->id);
1176
1177 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1178 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1179
1180 CRM_ASSERT(child_rsc);
1181 pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
1182 free_xml(child_rsc->xml);
1183 child_rsc->xml = NULL;
1184
1185 free_xml(child_rsc->orig_xml);
1186 child_rsc->orig_xml = NULL;
1187 child_rsc->fns->free(child_rsc);
1188 }
1189
1190 g_list_free(rsc->children);
1191
1192 if (clone_data) {
1193 CRM_ASSERT(clone_data->demote_notify == NULL);
1194 CRM_ASSERT(clone_data->stop_notify == NULL);
1195 CRM_ASSERT(clone_data->start_notify == NULL);
1196 CRM_ASSERT(clone_data->promote_notify == NULL);
1197 }
1198
1199 common_free(rsc);
1200 }
1201
1202 enum rsc_role_e
1203 clone_resource_state(const pe_resource_t * rsc, gboolean current)
1204 {
1205 enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN;
1206 GList *gIter = rsc->children;
1207
1208 for (; gIter != NULL; gIter = gIter->next) {
1209 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1210 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
1211
1212 if (a_role > clone_role) {
1213 clone_role = a_role;
1214 }
1215 }
1216
1217 pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(clone_role));
1218 return clone_role;
1219 }
1220
1221
1222
1223
1224
1225
1226
1227
1228 bool
1229 pe__is_universal_clone(const pe_resource_t *rsc,
1230 const pe_working_set_t *data_set)
1231 {
1232 if (pe_rsc_is_clone(rsc)) {
1233 clone_variant_data_t *clone_data = rsc->variant_opaque;
1234
1235 if (clone_data->clone_max == g_list_length(data_set->nodes)) {
1236 return TRUE;
1237 }
1238 }
1239 return FALSE;
1240 }
1241
1242 gboolean
1243 pe__clone_is_filtered(const pe_resource_t *rsc, GList *only_rsc,
1244 gboolean check_parent)
1245 {
1246 gboolean passes = FALSE;
1247 clone_variant_data_t *clone_data = NULL;
1248
1249 if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
1250 passes = TRUE;
1251 } else {
1252 get_clone_variant_data(clone_data, rsc);
1253 passes = pcmk__str_in_list(ID(clone_data->xml_obj_child), only_rsc, pcmk__str_star_matches);
1254
1255 if (!passes) {
1256 for (const GList *iter = rsc->children;
1257 iter != NULL; iter = iter->next) {
1258
1259 const pe_resource_t *child_rsc = NULL;
1260
1261 child_rsc = (const pe_resource_t *) iter->data;
1262 if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
1263 passes = TRUE;
1264 break;
1265 }
1266 }
1267 }
1268 }
1269 return !passes;
1270 }
1271
1272 const char *
1273 pe__clone_child_id(const pe_resource_t *rsc)
1274 {
1275 clone_variant_data_t *clone_data = NULL;
1276 get_clone_variant_data(clone_data, rsc);
1277 return ID(clone_data->xml_obj_child);
1278 }
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288 bool
1289 pe__clone_is_ordered(const pe_resource_t *clone)
1290 {
1291 clone_variant_data_t *clone_data = NULL;
1292
1293 get_clone_variant_data(clone_data, clone);
1294 return pcmk_is_set(clone_data->flags, pe__clone_ordered);
1295 }
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307 int
1308 pe__set_clone_flag(pe_resource_t *clone, enum pe__clone_flags flag)
1309 {
1310 clone_variant_data_t *clone_data = NULL;
1311
1312 get_clone_variant_data(clone_data, clone);
1313 if (pcmk_is_set(clone_data->flags, flag)) {
1314 return pcmk_rc_already;
1315 }
1316 clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
1317 "Clone", clone->id,
1318 clone_data->flags, flag, "flag");
1319 return pcmk_rc_ok;
1320 }
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330 void
1331 pe__create_promotable_pseudo_ops(pe_resource_t *clone, bool any_promoting,
1332 bool any_demoting)
1333 {
1334 pe_action_t *action = NULL;
1335 pe_action_t *action_complete = NULL;
1336 clone_variant_data_t *clone_data = NULL;
1337
1338 get_clone_variant_data(clone_data, clone);
1339
1340
1341 action = pe__new_rsc_pseudo_action(clone, RSC_PROMOTE, !any_promoting,
1342 true);
1343
1344
1345 action_complete = pe__new_rsc_pseudo_action(clone, RSC_PROMOTED,
1346 !any_promoting, true);
1347 action_complete->priority = INFINITY;
1348
1349
1350 if (clone_data->promote_notify == NULL) {
1351 clone_data->promote_notify = pe__action_notif_pseudo_ops(clone,
1352 RSC_PROMOTE,
1353 action,
1354 action_complete);
1355 }
1356
1357
1358 action = pe__new_rsc_pseudo_action(clone, RSC_DEMOTE, !any_demoting, true);
1359
1360
1361 action_complete = pe__new_rsc_pseudo_action(clone, RSC_DEMOTED,
1362 !any_demoting, true);
1363 action_complete->priority = INFINITY;
1364
1365
1366 if (clone_data->demote_notify == NULL) {
1367 clone_data->demote_notify = pe__action_notif_pseudo_ops(clone,
1368 RSC_DEMOTE,
1369 action,
1370 action_complete);
1371
1372 if (clone_data->promote_notify != NULL) {
1373 order_actions(clone_data->stop_notify->post_done,
1374 clone_data->promote_notify->pre,
1375 pe_order_optional);
1376 order_actions(clone_data->start_notify->post_done,
1377 clone_data->promote_notify->pre,
1378 pe_order_optional);
1379 order_actions(clone_data->demote_notify->post_done,
1380 clone_data->promote_notify->pre,
1381 pe_order_optional);
1382 order_actions(clone_data->demote_notify->post_done,
1383 clone_data->start_notify->pre,
1384 pe_order_optional);
1385 order_actions(clone_data->demote_notify->post_done,
1386 clone_data->stop_notify->pre,
1387 pe_order_optional);
1388 }
1389 }
1390 }
1391
1392
1393
1394
1395
1396
1397
1398 void
1399 pe__create_clone_notifications(pe_resource_t *clone)
1400 {
1401 clone_variant_data_t *clone_data = NULL;
1402
1403 get_clone_variant_data(clone_data, clone);
1404
1405 pe__create_action_notifications(clone, clone_data->start_notify);
1406 pe__create_action_notifications(clone, clone_data->stop_notify);
1407 pe__create_action_notifications(clone, clone_data->promote_notify);
1408 pe__create_action_notifications(clone, clone_data->demote_notify);
1409 }
1410
1411
1412
1413
1414
1415
1416
1417 void
1418 pe__free_clone_notification_data(pe_resource_t *clone)
1419 {
1420 clone_variant_data_t *clone_data = NULL;
1421
1422 get_clone_variant_data(clone_data, clone);
1423
1424 pe__free_action_notification_data(clone_data->demote_notify);
1425 clone_data->demote_notify = NULL;
1426
1427 pe__free_action_notification_data(clone_data->stop_notify);
1428 clone_data->stop_notify = NULL;
1429
1430 pe__free_action_notification_data(clone_data->start_notify);
1431 clone_data->start_notify = NULL;
1432
1433 pe__free_action_notification_data(clone_data->promote_notify);
1434 clone_data->promote_notify = NULL;
1435 }
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447 void
1448 pe__create_clone_notif_pseudo_ops(pe_resource_t *clone,
1449 pe_action_t *start, pe_action_t *started,
1450 pe_action_t *stop, pe_action_t *stopped)
1451 {
1452 clone_variant_data_t *clone_data = NULL;
1453
1454 get_clone_variant_data(clone_data, clone);
1455
1456 if (clone_data->start_notify == NULL) {
1457 clone_data->start_notify = pe__action_notif_pseudo_ops(clone, RSC_START,
1458 start, started);
1459 }
1460
1461 if (clone_data->stop_notify == NULL) {
1462 clone_data->stop_notify = pe__action_notif_pseudo_ops(clone, RSC_STOP,
1463 stop, stopped);
1464 if ((clone_data->start_notify != NULL)
1465 && (clone_data->stop_notify != NULL)) {
1466 order_actions(clone_data->stop_notify->post_done,
1467 clone_data->start_notify->pre, pe_order_optional);
1468 }
1469 }
1470 }