This source file includes following definitions.
- 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
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 #define VARIANT_CLONE 1
23 #include "./variant.h"
24
25 #ifdef PCMK__COMPAT_2_0
26 #define PROMOTED_INSTANCES RSC_ROLE_PROMOTED_LEGACY_S "s"
27 #define UNPROMOTED_INSTANCES RSC_ROLE_UNPROMOTED_LEGACY_S "s"
28 #else
29 #define PROMOTED_INSTANCES RSC_ROLE_PROMOTED_S
30 #define UNPROMOTED_INSTANCES RSC_ROLE_UNPROMOTED_S
31 #endif
32
33 static GList *
34 sorted_hash_table_values(GHashTable *table)
35 {
36 GList *retval = NULL;
37 GHashTableIter iter;
38 gpointer key, value;
39
40 g_hash_table_iter_init(&iter, table);
41 while (g_hash_table_iter_next(&iter, &key, &value)) {
42 if (!g_list_find_custom(retval, value, (GCompareFunc) strcmp)) {
43 retval = g_list_prepend(retval, (char *) value);
44 }
45 }
46
47 retval = g_list_sort(retval, (GCompareFunc) strcmp);
48 return retval;
49 }
50
51 static GList *
52 nodes_with_status(GHashTable *table, const char *status)
53 {
54 GList *retval = NULL;
55 GHashTableIter iter;
56 gpointer key, value;
57
58 g_hash_table_iter_init(&iter, table);
59 while (g_hash_table_iter_next(&iter, &key, &value)) {
60 if (!strcmp((char *) value, status)) {
61 retval = g_list_prepend(retval, key);
62 }
63 }
64
65 retval = g_list_sort(retval, (GCompareFunc) pcmk__numeric_strcasecmp);
66 return retval;
67 }
68
69 static char *
70 node_list_to_str(GList *list)
71 {
72 char *retval = NULL;
73 size_t len = 0;
74
75 for (GList *iter = list; iter != NULL; iter = iter->next) {
76 pcmk__add_word(&retval, &len, (char *) iter->data);
77 }
78
79 return retval;
80 }
81
82 static void
83 clone_header(pcmk__output_t *out, int *rc, pe_resource_t *rsc, clone_variant_data_t *clone_data)
84 {
85 char *attrs = NULL;
86 size_t len = 0;
87
88 if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
89 pcmk__add_separated_word(&attrs, &len, "promotable", ", ");
90 }
91
92 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
93 pcmk__add_separated_word(&attrs, &len, "unique", ", ");
94 }
95
96 if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
97 pcmk__add_separated_word(&attrs, &len, "unmanaged", ", ");
98 }
99
100 if (pe__resource_is_disabled(rsc)) {
101 pcmk__add_separated_word(&attrs, &len, "disabled", ", ");
102 }
103
104 if (attrs) {
105 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)",
106 rsc->id, ID(clone_data->xml_obj_child),
107 attrs);
108 free(attrs);
109 } else {
110 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]",
111 rsc->id, ID(clone_data->xml_obj_child))
112 }
113 }
114
115 void
116 pe__force_anon(const char *standard, pe_resource_t *rsc, const char *rid,
117 pe_working_set_t *data_set)
118 {
119 if (pe_rsc_is_clone(rsc)) {
120 clone_variant_data_t *clone_data = NULL;
121
122 get_clone_variant_data(clone_data, rsc);
123
124 pe_warn("Ignoring " XML_RSC_ATTR_UNIQUE " for %s because %s resources "
125 "such as %s can be used only as anonymous clones",
126 rsc->id, standard, rid);
127
128 clone_data->clone_node_max = 1;
129 clone_data->clone_max = QB_MIN(clone_data->clone_max,
130 g_list_length(data_set->nodes));
131 }
132 }
133
134 pe_resource_t *
135 find_clone_instance(pe_resource_t * rsc, const char *sub_id, pe_working_set_t * data_set)
136 {
137 char *child_id = NULL;
138 pe_resource_t *child = NULL;
139 const char *child_base = NULL;
140 clone_variant_data_t *clone_data = NULL;
141
142 get_clone_variant_data(clone_data, rsc);
143
144 child_base = ID(clone_data->xml_obj_child);
145 child_id = crm_strdup_printf("%s:%s", child_base, sub_id);
146 child = pe_find_resource(rsc->children, child_id);
147
148 free(child_id);
149 return child;
150 }
151
152 pe_resource_t *
153 pe__create_clone_child(pe_resource_t *rsc, pe_working_set_t *data_set)
154 {
155 gboolean as_orphan = FALSE;
156 char *inc_num = NULL;
157 char *inc_max = NULL;
158 pe_resource_t *child_rsc = NULL;
159 xmlNode *child_copy = NULL;
160 clone_variant_data_t *clone_data = NULL;
161
162 get_clone_variant_data(clone_data, rsc);
163
164 CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
165
166 if (clone_data->total_clones >= clone_data->clone_max) {
167
168 as_orphan = TRUE;
169 }
170
171
172 inc_num = pcmk__itoa(clone_data->total_clones);
173 inc_max = pcmk__itoa(clone_data->clone_max);
174
175 child_copy = copy_xml(clone_data->xml_obj_child);
176
177 crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num);
178
179 if (common_unpack(child_copy, &child_rsc, rsc, data_set) == FALSE) {
180 pe_err("Failed unpacking resource %s", crm_element_value(child_copy, XML_ATTR_ID));
181 child_rsc = NULL;
182 goto bail;
183 }
184
185
186 CRM_ASSERT(child_rsc);
187 clone_data->total_clones += 1;
188 pe_rsc_trace(child_rsc, "Setting clone attributes for: %s", child_rsc->id);
189 rsc->children = g_list_append(rsc->children, child_rsc);
190 if (as_orphan) {
191 pe__set_resource_flags_recursive(child_rsc, pe_rsc_orphan);
192 }
193
194 add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max);
195 pe_rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
196
197 bail:
198 free(inc_num);
199 free(inc_max);
200
201 return child_rsc;
202 }
203
204 gboolean
205 clone_unpack(pe_resource_t * rsc, pe_working_set_t * data_set)
206 {
207 int lpc = 0;
208 xmlNode *a_child = NULL;
209 xmlNode *xml_obj = rsc->xml;
210 clone_variant_data_t *clone_data = NULL;
211
212 const char *ordered = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_ORDERED);
213 const char *max_clones = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_MAX);
214 const char *max_clones_node = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX);
215
216 pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
217
218 clone_data = calloc(1, sizeof(clone_variant_data_t));
219 rsc->variant_opaque = clone_data;
220
221 if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
222 const char *promoted_max = NULL;
223 const char *promoted_node_max = NULL;
224
225 promoted_max = g_hash_table_lookup(rsc->meta,
226 XML_RSC_ATTR_PROMOTED_MAX);
227 if (promoted_max == NULL) {
228
229 promoted_max = g_hash_table_lookup(rsc->meta,
230 PCMK_XE_PROMOTED_MAX_LEGACY);
231 }
232
233 promoted_node_max = g_hash_table_lookup(rsc->meta,
234 XML_RSC_ATTR_PROMOTED_NODEMAX);
235 if (promoted_node_max == NULL) {
236
237 promoted_node_max = g_hash_table_lookup(rsc->meta,
238 PCMK_XE_PROMOTED_NODE_MAX_LEGACY);
239 }
240
241
242 if (promoted_max == NULL) {
243 clone_data->promoted_max = 1;
244 } else {
245 pcmk__scan_min_int(promoted_max, &(clone_data->promoted_max), 0);
246 }
247
248
249 if (promoted_node_max == NULL) {
250 clone_data->promoted_node_max = 1;
251 } else {
252 pcmk__scan_min_int(promoted_node_max,
253 &(clone_data->promoted_node_max), 0);
254 }
255 }
256
257
258
259
260
261 if (max_clones_node == NULL) {
262 clone_data->clone_node_max = 1;
263 } else {
264 pcmk__scan_min_int(max_clones_node, &(clone_data->clone_node_max), 0);
265 }
266
267
268
269
270 if (max_clones == NULL) {
271 clone_data->clone_max = QB_MAX(1, g_list_length(data_set->nodes));
272 } else {
273 pcmk__scan_min_int(max_clones, &(clone_data->clone_max), 0);
274 }
275
276 clone_data->ordered = crm_is_true(ordered);
277
278 if ((rsc->flags & pe_rsc_unique) == 0 && clone_data->clone_node_max > 1) {
279 pcmk__config_err("Ignoring " XML_RSC_ATTR_PROMOTED_MAX " for %s "
280 "because anonymous clones support only one instance "
281 "per node", rsc->id);
282 clone_data->clone_node_max = 1;
283 }
284
285 pe_rsc_trace(rsc, "Options for %s", rsc->id);
286 pe_rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
287 pe_rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
288 pe_rsc_trace(rsc, "\tClone is unique: %s",
289 pe__rsc_bool_str(rsc, pe_rsc_unique));
290 pe_rsc_trace(rsc, "\tClone is promotable: %s",
291 pe__rsc_bool_str(rsc, pe_rsc_promotable));
292
293
294 for (a_child = pcmk__xe_first_child(xml_obj); a_child != NULL;
295 a_child = pcmk__xe_next(a_child)) {
296
297 if (pcmk__str_any_of((const char *)a_child->name, XML_CIB_TAG_RESOURCE, XML_CIB_TAG_GROUP, NULL)) {
298 clone_data->xml_obj_child = a_child;
299 break;
300 }
301 }
302
303 if (clone_data->xml_obj_child == NULL) {
304 pcmk__config_err("%s has nothing to clone", rsc->id);
305 return FALSE;
306 }
307
308
309
310
311
312
313
314 if (g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_STICKINESS) == NULL) {
315 add_hash_param(rsc->meta, XML_RSC_ATTR_STICKINESS, "1");
316 }
317
318
319
320
321 add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE,
322 pe__rsc_bool_str(rsc, pe_rsc_unique));
323
324 if (clone_data->clone_max <= 0) {
325
326
327
328 if (pe__create_clone_child(rsc, data_set) == NULL) {
329 return FALSE;
330 }
331
332 } else {
333
334 for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
335 if (pe__create_clone_child(rsc, data_set) == NULL) {
336 return FALSE;
337 }
338 }
339 }
340
341 pe_rsc_trace(rsc, "Added %d children to resource %s...", clone_data->clone_max, rsc->id);
342 return TRUE;
343 }
344
345 gboolean
346 clone_active(pe_resource_t * rsc, gboolean all)
347 {
348 GList *gIter = rsc->children;
349
350 for (; gIter != NULL; gIter = gIter->next) {
351 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
352 gboolean child_active = child_rsc->fns->active(child_rsc, all);
353
354 if (all == FALSE && child_active) {
355 return TRUE;
356 } else if (all && child_active == FALSE) {
357 return FALSE;
358 }
359 }
360
361 if (all) {
362 return TRUE;
363 } else {
364 return FALSE;
365 }
366 }
367
368 static void
369 short_print(char *list, const char *prefix, const char *type, const char *suffix, long options, void *print_data)
370 {
371 if(suffix == NULL) {
372 suffix = "";
373 }
374
375 if (list) {
376 if (options & pe_print_html) {
377 status_print("<li>");
378 }
379 status_print("%s%s: [ %s ]%s", prefix, type, list, suffix);
380
381 if (options & pe_print_html) {
382 status_print("</li>\n");
383
384 } else if (options & pe_print_suppres_nl) {
385
386 } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
387 status_print("\n");
388 }
389
390 }
391 }
392
393 static const char *
394 configured_role_str(pe_resource_t * rsc)
395 {
396 const char *target_role = g_hash_table_lookup(rsc->meta,
397 XML_RSC_ATTR_TARGET_ROLE);
398
399 if ((target_role == NULL) && rsc->children && rsc->children->data) {
400 target_role = g_hash_table_lookup(((pe_resource_t*)rsc->children->data)->meta,
401 XML_RSC_ATTR_TARGET_ROLE);
402 }
403 return target_role;
404 }
405
406 static enum rsc_role_e
407 configured_role(pe_resource_t * rsc)
408 {
409 const char *target_role = configured_role_str(rsc);
410
411 if (target_role) {
412 return text2role(target_role);
413 }
414 return RSC_ROLE_UNKNOWN;
415 }
416
417 static void
418 clone_print_xml(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
419 {
420 char *child_text = crm_strdup_printf("%s ", pre_text);
421 const char *target_role = configured_role_str(rsc);
422 GList *gIter = rsc->children;
423
424 status_print("%s<clone ", pre_text);
425 status_print("id=\"%s\" ", rsc->id);
426 status_print("multi_state=\"%s\" ",
427 pe__rsc_bool_str(rsc, pe_rsc_promotable));
428 status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_unique));
429 status_print("managed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_managed));
430 status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_failed));
431 status_print("failure_ignored=\"%s\" ",
432 pe__rsc_bool_str(rsc, pe_rsc_failure_ignored));
433 if (target_role) {
434 status_print("target_role=\"%s\" ", target_role);
435 }
436 status_print(">\n");
437
438 for (; gIter != NULL; gIter = gIter->next) {
439 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
440
441 child_rsc->fns->print(child_rsc, child_text, options, print_data);
442 }
443
444 status_print("%s</clone>\n", pre_text);
445 free(child_text);
446 }
447
448 bool is_set_recursive(pe_resource_t * rsc, long long flag, bool any)
449 {
450 GList *gIter;
451 bool all = !any;
452
453 if (pcmk_is_set(rsc->flags, flag)) {
454 if(any) {
455 return TRUE;
456 }
457 } else if(all) {
458 return FALSE;
459 }
460
461 for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
462 if(is_set_recursive(gIter->data, flag, any)) {
463 if(any) {
464 return TRUE;
465 }
466
467 } else if(all) {
468 return FALSE;
469 }
470 }
471
472 if(all) {
473 return TRUE;
474 }
475 return FALSE;
476 }
477
478 void
479 clone_print(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
480 {
481 char *list_text = NULL;
482 char *child_text = NULL;
483 char *stopped_list = NULL;
484 size_t list_text_len = 0;
485 size_t stopped_list_len = 0;
486
487 GList *promoted_list = NULL;
488 GList *started_list = NULL;
489 GList *gIter = rsc->children;
490
491 clone_variant_data_t *clone_data = NULL;
492 int active_instances = 0;
493
494 if (pre_text == NULL) {
495 pre_text = " ";
496 }
497
498 if (options & pe_print_xml) {
499 clone_print_xml(rsc, pre_text, options, print_data);
500 return;
501 }
502
503 get_clone_variant_data(clone_data, rsc);
504
505 child_text = crm_strdup_printf("%s ", pre_text);
506
507 status_print("%sClone Set: %s [%s]%s%s%s",
508 pre_text ? pre_text : "", rsc->id, ID(clone_data->xml_obj_child),
509 pcmk_is_set(rsc->flags, pe_rsc_promotable)? " (promotable)" : "",
510 pcmk_is_set(rsc->flags, pe_rsc_unique)? " (unique)" : "",
511 pcmk_is_set(rsc->flags, pe_rsc_managed)? "" : " (unmanaged)");
512
513 if (options & pe_print_html) {
514 status_print("\n<ul>\n");
515
516 } else if ((options & pe_print_log) == 0) {
517 status_print("\n");
518 }
519
520 for (; gIter != NULL; gIter = gIter->next) {
521 gboolean print_full = FALSE;
522 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
523 gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
524
525 if (options & pe_print_clone_details) {
526 print_full = TRUE;
527 }
528
529 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
530
531 if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
532 print_full = TRUE;
533 }
534
535
536
537 } else if (pcmk_is_set(options, pe_print_pending)
538 && (child_rsc->pending_task != NULL)
539 && strcmp(child_rsc->pending_task, "probe")) {
540
541 print_full = TRUE;
542
543 } else if (partially_active == FALSE) {
544
545 if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
546 && !pcmk_is_set(options, pe_print_clone_active)) {
547 pcmk__add_word(&stopped_list, &stopped_list_len, child_rsc->id);
548 }
549
550 } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
551 || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
552 || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
553
554
555 print_full = TRUE;
556
557 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
558
559
560 pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
561
562 if (location) {
563
564
565 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
566
567 if (location->details->online == FALSE && location->details->unclean) {
568 print_full = TRUE;
569
570 } else if (a_role > RSC_ROLE_UNPROMOTED) {
571 promoted_list = g_list_append(promoted_list, location);
572
573 } else {
574 started_list = g_list_append(started_list, location);
575 }
576
577 } else {
578
579 print_full = TRUE;
580 }
581
582 } else {
583
584 print_full = TRUE;
585 }
586
587 if (print_full) {
588 if (options & pe_print_html) {
589 status_print("<li>\n");
590 }
591 child_rsc->fns->print(child_rsc, child_text, options, print_data);
592 if (options & pe_print_html) {
593 status_print("</li>\n");
594 }
595 }
596 }
597
598
599 promoted_list = g_list_sort(promoted_list, sort_node_uname);
600 for (gIter = promoted_list; gIter; gIter = gIter->next) {
601 pe_node_t *host = gIter->data;
602
603 pcmk__add_word(&list_text, &list_text_len, host->details->uname);
604 active_instances++;
605 }
606
607 short_print(list_text, child_text, PROMOTED_INSTANCES, NULL, options,
608 print_data);
609 g_list_free(promoted_list);
610 free(list_text);
611 list_text = NULL;
612 list_text_len = 0;
613
614
615 started_list = g_list_sort(started_list, sort_node_uname);
616 for (gIter = started_list; gIter; gIter = gIter->next) {
617 pe_node_t *host = gIter->data;
618
619 pcmk__add_word(&list_text, &list_text_len, host->details->uname);
620 active_instances++;
621 }
622
623 if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
624 enum rsc_role_e role = configured_role(rsc);
625
626 if (role == RSC_ROLE_UNPROMOTED) {
627 short_print(list_text, child_text,
628 UNPROMOTED_INSTANCES " (target-role)", NULL, options,
629 print_data);
630 } else {
631 short_print(list_text, child_text, UNPROMOTED_INSTANCES, NULL,
632 options, print_data);
633 }
634
635 } else {
636 short_print(list_text, child_text, "Started", NULL, options, print_data);
637 }
638
639 g_list_free(started_list);
640 free(list_text);
641 list_text = NULL;
642 list_text_len = 0;
643
644 if (!pcmk_is_set(options, pe_print_clone_active)) {
645 const char *state = "Stopped";
646 enum rsc_role_e role = configured_role(rsc);
647
648 if (role == RSC_ROLE_STOPPED) {
649 state = "Stopped (disabled)";
650 }
651
652 if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
653 && (clone_data->clone_max > active_instances)) {
654
655 GList *nIter;
656 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
657
658
659 free(stopped_list);
660 stopped_list = NULL;
661 stopped_list_len = 0;
662
663 if (list == NULL) {
664
665
666
667 list = g_hash_table_get_values(rsc->known_on);
668 }
669
670 list = g_list_sort(list, sort_node_uname);
671 for (nIter = list; nIter != NULL; nIter = nIter->next) {
672 pe_node_t *node = (pe_node_t *)nIter->data;
673
674 if (pe_find_node(rsc->running_on, node->details->uname) == NULL) {
675 pcmk__add_word(&stopped_list, &stopped_list_len,
676 node->details->uname);
677 }
678 }
679 g_list_free(list);
680 }
681
682 short_print(stopped_list, child_text, state, NULL, options, print_data);
683 free(stopped_list);
684 }
685
686 if (options & pe_print_html) {
687 status_print("</ul>\n");
688 }
689
690 free(child_text);
691 }
692
693 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pe_resource_t *", "GList *", "GList *")
694 int
695 pe__clone_xml(pcmk__output_t *out, va_list args)
696 {
697 uint32_t show_opts = va_arg(args, uint32_t);
698 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
699 GList *only_node = va_arg(args, GList *);
700 GList *only_rsc = va_arg(args, GList *);
701
702 GList *gIter = rsc->children;
703 GList *all = NULL;
704 int rc = pcmk_rc_no_output;
705 gboolean printed_header = FALSE;
706 gboolean print_everything = TRUE;
707
708 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
709 return rc;
710 }
711
712 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
713 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
714
715 all = g_list_prepend(all, (gpointer) "*");
716
717 for (; gIter != NULL; gIter = gIter->next) {
718 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
719
720 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
721 continue;
722 }
723
724 if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
725 continue;
726 }
727
728 if (!printed_header) {
729 printed_header = TRUE;
730
731 rc = pe__name_and_nvpairs_xml(out, true, "clone", 8,
732 "id", rsc->id,
733 "multi_state", pe__rsc_bool_str(rsc, pe_rsc_promotable),
734 "unique", pe__rsc_bool_str(rsc, pe_rsc_unique),
735 "managed", pe__rsc_bool_str(rsc, pe_rsc_managed),
736 "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)),
737 "failed", pe__rsc_bool_str(rsc, pe_rsc_failed),
738 "failure_ignored", pe__rsc_bool_str(rsc, pe_rsc_failure_ignored),
739 "target_role", configured_role_str(rsc));
740 CRM_ASSERT(rc == pcmk_rc_ok);
741 }
742
743 out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
744 child_rsc, only_node, all);
745 }
746
747 if (printed_header) {
748 pcmk__output_xml_pop_parent(out);
749 }
750
751 g_list_free(all);
752 return rc;
753 }
754
755 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pe_resource_t *", "GList *", "GList *")
756 int
757 pe__clone_default(pcmk__output_t *out, va_list args)
758 {
759 uint32_t show_opts = va_arg(args, uint32_t);
760 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
761 GList *only_node = va_arg(args, GList *);
762 GList *only_rsc = va_arg(args, GList *);
763
764 GHashTable *stopped = NULL;
765
766 char *list_text = NULL;
767 size_t list_text_len = 0;
768
769 GList *promoted_list = NULL;
770 GList *started_list = NULL;
771 GList *gIter = rsc->children;
772
773 clone_variant_data_t *clone_data = NULL;
774 int active_instances = 0;
775 int rc = pcmk_rc_no_output;
776 gboolean print_everything = TRUE;
777
778 get_clone_variant_data(clone_data, rsc);
779
780 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
781 return rc;
782 }
783
784 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
785 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
786
787 for (; gIter != NULL; gIter = gIter->next) {
788 gboolean print_full = FALSE;
789 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
790 gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
791
792 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
793 continue;
794 }
795
796 if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
797 continue;
798 }
799
800 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
801 print_full = TRUE;
802 }
803
804 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
805
806 if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
807 print_full = TRUE;
808 }
809
810
811
812 } else if (pcmk_is_set(show_opts, pcmk_show_pending)
813 && (child_rsc->pending_task != NULL)
814 && strcmp(child_rsc->pending_task, "probe")) {
815
816 print_full = TRUE;
817
818 } else if (partially_active == FALSE) {
819
820 if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
821 && !pcmk_is_set(show_opts, pcmk_show_clone_detail)
822 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
823 if (stopped == NULL) {
824 stopped = pcmk__strkey_table(free, free);
825 }
826 g_hash_table_insert(stopped, strdup(child_rsc->id), strdup("Stopped"));
827 }
828
829 } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
830 || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
831 || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
832
833
834 print_full = TRUE;
835
836 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
837
838
839 pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
840
841 if (location) {
842
843
844 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
845
846 if (location->details->online == FALSE && location->details->unclean) {
847 print_full = TRUE;
848
849 } else if (a_role > RSC_ROLE_UNPROMOTED) {
850 promoted_list = g_list_append(promoted_list, location);
851
852 } else {
853 started_list = g_list_append(started_list, location);
854 }
855
856 } else {
857
858 print_full = TRUE;
859 }
860
861 } else {
862
863 print_full = TRUE;
864 }
865
866 if (print_full) {
867 GList *all = NULL;
868
869 clone_header(out, &rc, rsc, clone_data);
870
871
872 all = g_list_prepend(all, (gpointer) "*");
873 out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
874 child_rsc, only_node, all);
875 g_list_free(all);
876 }
877 }
878
879 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
880 PCMK__OUTPUT_LIST_FOOTER(out, rc);
881 return pcmk_rc_ok;
882 }
883
884
885 promoted_list = g_list_sort(promoted_list, sort_node_uname);
886 for (gIter = promoted_list; gIter; gIter = gIter->next) {
887 pe_node_t *host = gIter->data;
888
889 if (!pcmk__str_in_list(host->details->uname, only_node,
890 pcmk__str_star_matches|pcmk__str_casei)) {
891 continue;
892 }
893
894 pcmk__add_word(&list_text, &list_text_len, host->details->uname);
895 active_instances++;
896 }
897 g_list_free(promoted_list);
898
899 if (list_text != NULL) {
900 clone_header(out, &rc, rsc, clone_data);
901
902 out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]", list_text);
903 free(list_text);
904 list_text = NULL;
905 list_text_len = 0;
906 }
907
908
909 started_list = g_list_sort(started_list, sort_node_uname);
910 for (gIter = started_list; gIter; gIter = gIter->next) {
911 pe_node_t *host = gIter->data;
912
913 if (!pcmk__str_in_list(host->details->uname, only_node,
914 pcmk__str_star_matches|pcmk__str_casei)) {
915 continue;
916 }
917
918 pcmk__add_word(&list_text, &list_text_len, host->details->uname);
919 active_instances++;
920 }
921 g_list_free(started_list);
922
923 if (list_text != NULL) {
924 clone_header(out, &rc, rsc, clone_data);
925
926 if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
927 enum rsc_role_e role = configured_role(rsc);
928
929 if (role == RSC_ROLE_UNPROMOTED) {
930 out->list_item(out, NULL,
931 UNPROMOTED_INSTANCES " (target-role): [ %s ]",
932 list_text);
933 } else {
934 out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
935 list_text);
936 }
937
938 } else {
939 out->list_item(out, NULL, "Started: [ %s ]", list_text);
940 }
941 free(list_text);
942 list_text = NULL;
943 list_text_len = 0;
944 }
945
946 if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
947 if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
948 && (clone_data->clone_max > active_instances)) {
949
950 GList *nIter;
951 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
952
953
954 if (stopped != NULL) {
955 g_hash_table_destroy(stopped);
956 stopped = NULL;
957 }
958
959 if (list == NULL) {
960
961
962
963 list = g_hash_table_get_values(rsc->known_on);
964 }
965
966 list = g_list_sort(list, sort_node_uname);
967 for (nIter = list; nIter != NULL; nIter = nIter->next) {
968 pe_node_t *node = (pe_node_t *)nIter->data;
969
970 if (pe_find_node(rsc->running_on, node->details->uname) == NULL &&
971 pcmk__str_in_list(node->details->uname, only_node,
972 pcmk__str_star_matches|pcmk__str_casei)) {
973 xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node->details->uname);
974 const char *state = "Stopped";
975
976 if (configured_role(rsc) == RSC_ROLE_STOPPED) {
977 state = "Stopped (disabled)";
978 }
979
980 if (stopped == NULL) {
981 stopped = pcmk__strkey_table(free, free);
982 }
983 if (probe_op != NULL) {
984 int rc;
985
986 pcmk__scan_min_int(crm_element_value(probe_op, XML_LRM_ATTR_RC), &rc, 0);
987 g_hash_table_insert(stopped, strdup(node->details->uname),
988 crm_strdup_printf("Stopped (%s)", services_ocf_exitcode_str(rc)));
989 } else {
990 g_hash_table_insert(stopped, strdup(node->details->uname),
991 strdup(state));
992 }
993 }
994 }
995 g_list_free(list);
996 }
997
998 if (stopped != NULL) {
999 GList *list = sorted_hash_table_values(stopped);
1000
1001 clone_header(out, &rc, rsc, clone_data);
1002
1003 for (GList *status_iter = list; status_iter != NULL; status_iter = status_iter->next) {
1004 const char *status = status_iter->data;
1005 GList *nodes = nodes_with_status(stopped, status);
1006 char *str = node_list_to_str(nodes);
1007
1008 if (str != NULL) {
1009 out->list_item(out, NULL, "%s: [ %s ]", status, str);
1010 free(str);
1011 }
1012
1013 g_list_free(nodes);
1014 }
1015
1016 g_list_free(list);
1017 g_hash_table_destroy(stopped);
1018
1019
1020
1021
1022
1023 } else if (active_instances == 0) {
1024 clone_header(out, &rc, rsc, clone_data);
1025 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1026 return rc;
1027 }
1028 }
1029
1030 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1031 return rc;
1032 }
1033
1034 void
1035 clone_free(pe_resource_t * rsc)
1036 {
1037 clone_variant_data_t *clone_data = NULL;
1038
1039 get_clone_variant_data(clone_data, rsc);
1040
1041 pe_rsc_trace(rsc, "Freeing %s", rsc->id);
1042
1043 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1044 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1045
1046 CRM_ASSERT(child_rsc);
1047 pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
1048 free_xml(child_rsc->xml);
1049 child_rsc->xml = NULL;
1050
1051 free_xml(child_rsc->orig_xml);
1052 child_rsc->orig_xml = NULL;
1053 child_rsc->fns->free(child_rsc);
1054 }
1055
1056 g_list_free(rsc->children);
1057
1058 if (clone_data) {
1059 CRM_ASSERT(clone_data->demote_notify == NULL);
1060 CRM_ASSERT(clone_data->stop_notify == NULL);
1061 CRM_ASSERT(clone_data->start_notify == NULL);
1062 CRM_ASSERT(clone_data->promote_notify == NULL);
1063 }
1064
1065 common_free(rsc);
1066 }
1067
1068 enum rsc_role_e
1069 clone_resource_state(const pe_resource_t * rsc, gboolean current)
1070 {
1071 enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN;
1072 GList *gIter = rsc->children;
1073
1074 for (; gIter != NULL; gIter = gIter->next) {
1075 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1076 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
1077
1078 if (a_role > clone_role) {
1079 clone_role = a_role;
1080 }
1081 }
1082
1083 pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(clone_role));
1084 return clone_role;
1085 }
1086
1087
1088
1089
1090
1091
1092
1093
1094 bool
1095 pe__is_universal_clone(pe_resource_t *rsc,
1096 pe_working_set_t *data_set)
1097 {
1098 if (pe_rsc_is_clone(rsc)) {
1099 clone_variant_data_t *clone_data = NULL;
1100
1101 get_clone_variant_data(clone_data, rsc);
1102 if (clone_data->clone_max == g_list_length(data_set->nodes)) {
1103 return TRUE;
1104 }
1105 }
1106 return FALSE;
1107 }
1108
1109 gboolean
1110 pe__clone_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
1111 {
1112 gboolean passes = FALSE;
1113 clone_variant_data_t *clone_data = NULL;
1114
1115 if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
1116 passes = TRUE;
1117 } else {
1118 get_clone_variant_data(clone_data, rsc);
1119 passes = pcmk__str_in_list(ID(clone_data->xml_obj_child), only_rsc, pcmk__str_star_matches);
1120
1121 if (!passes) {
1122 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1123 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1124
1125 if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
1126 passes = TRUE;
1127 break;
1128 }
1129 }
1130 }
1131 }
1132
1133 return !passes;
1134 }
1135
1136 const char *
1137 pe__clone_child_id(pe_resource_t *rsc)
1138 {
1139 clone_variant_data_t *clone_data = NULL;
1140 get_clone_variant_data(clone_data, rsc);
1141 return ID(clone_data->xml_obj_child);
1142 }