pacemaker 2.1.4-dc6eb4362e
Scalable High-Availability cluster resource manager
clone.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
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>
17#include <pe_status_private.h>
18#include <crm/msg_xml.h>
19#include <crm/common/output.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
33static GList *
34sorted_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
51static GList *
52nodes_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
69static char *
70node_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
82static void
83clone_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
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
115void
116pe__force_anon(const char *standard, pe_resource_t *rsc, const char *rid,
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
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
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 // If we've already used all available instances, this is an orphan
168 as_orphan = TRUE;
169 }
170
171 // Allocate instance numbers in numerical order (starting at 0)
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/* child_rsc->globally_unique = rsc->globally_unique; */
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) {
192 }
193
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
204gboolean
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
222 const char *promoted_max = NULL;
223 const char *promoted_node_max = NULL;
224
225 promoted_max = g_hash_table_lookup(rsc->meta,
227 if (promoted_max == NULL) {
228 // @COMPAT deprecated since 2.0.0
229 promoted_max = g_hash_table_lookup(rsc->meta,
231 }
232
233 promoted_node_max = g_hash_table_lookup(rsc->meta,
235 if (promoted_node_max == NULL) {
236 // @COMPAT deprecated since 2.0.0
237 promoted_node_max = g_hash_table_lookup(rsc->meta,
239 }
240
241 // Use 1 as default but 0 for minimum and invalid
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 // Use 1 as default but 0 for minimum and invalid
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 // Implied by calloc()
258 /* clone_data->xml_obj_child = NULL; */
259
260 // Use 1 as default but 0 for minimum and invalid
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 /* Use number of nodes (but always at least 1, which is handy for crm_verify
268 * for a CIB without nodes) as default, but 0 for minimum and invalid
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 // Clones may contain a single group or primitive
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 * Make clones ever so slightly sticky by default
310 *
311 * This helps ensure clone instances are not shuffled around the cluster
312 * for no benefit in situations when pre-allocation is not appropriate
313 */
314 if (g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_STICKINESS) == NULL) {
316 }
317
318 /* This ensures that the globally-unique value always exists for children to
319 * inherit when being unpacked, as well as in resource agents' environment.
320 */
322 pe__rsc_bool_str(rsc, pe_rsc_unique));
323
324 if (clone_data->clone_max <= 0) {
325 /* Create one child instance so that unpack_find_resource() will hook up
326 * any orphans up to the parent correctly.
327 */
328 if (pe__create_clone_child(rsc, data_set) == NULL) {
329 return FALSE;
330 }
331
332 } else {
333 // Create a child instance for each available instance number
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
345gboolean
346clone_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
368static void
369short_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 /* nothing */
386 } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
387 status_print("\n");
388 }
389
390 }
391}
392
393static const char *
394configured_role_str(pe_resource_t * rsc)
395{
396 const char *target_role = g_hash_table_lookup(rsc->meta,
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,
402 }
403 return target_role;
404}
405
406static enum rsc_role_e
407configured_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
417static void
418clone_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
448bool 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
478void
479clone_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 // Print individual instance when unique (except stopped orphans)
531 if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
532 print_full = TRUE;
533 }
534
535 // Everything else in this block is for anonymous clones
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 // Print individual instance when non-probe action is pending
541 print_full = TRUE;
542
543 } else if (partially_active == FALSE) {
544 // List stopped instances when requested (except orphans)
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 // Print individual instance when active orphaned/unmanaged/failed
555 print_full = TRUE;
556
557 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
558 // Instance of fully active anonymous clone
559
560 pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
561
562 if (location) {
563 // Instance is active on a single node
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 /* uncolocated group - bleh */
579 print_full = TRUE;
580 }
581
582 } else {
583 // Instance of partially active anonymous clone
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 /* Promoted */
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 /* Started/Unpromoted */
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
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
653 && (clone_data->clone_max > active_instances)) {
654
655 GList *nIter;
656 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
657
658 /* Custom stopped list for non-unique clones */
659 free(stopped_list);
660 stopped_list = NULL;
661 stopped_list_len = 0;
662
663 if (list == NULL) {
664 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
665 * If we've not probed for them yet, the Stopped list will be empty
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
693PCMK__OUTPUT_ARGS("clone", "uint32_t", "pe_resource_t *", "GList *", "GList *")
694int
695pe__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) {
749 }
750
751 g_list_free(all);
752 return rc;
753}
754
755PCMK__OUTPUT_ARGS("clone", "uint32_t", "pe_resource_t *", "GList *", "GList *")
756int
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 // Print individual instance when unique (except stopped orphans)
806 if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
807 print_full = TRUE;
808 }
809
810 // Everything else in this block is for anonymous clones
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 // Print individual instance when non-probe action is pending
816 print_full = TRUE;
817
818 } else if (partially_active == FALSE) {
819 // List stopped instances when requested (except orphans)
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 // Print individual instance when active orphaned/unmanaged/failed
834 print_full = TRUE;
835
836 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
837 // Instance of fully active anonymous clone
838
839 pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
840
841 if (location) {
842 // Instance is active on a single node
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 /* uncolocated group - bleh */
858 print_full = TRUE;
859 }
860
861 } else {
862 // Instance of partially active anonymous clone
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 /* Print every resource that's a child of this clone. */
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)) {
881 return pcmk_rc_ok;
882 }
883
884 /* Promoted */
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,
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 /* Started/Unpromoted */
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,
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
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)) {
948 && (clone_data->clone_max > active_instances)) {
949
950 GList *nIter;
951 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
952
953 /* Custom stopped table for non-unique clones */
954 if (stopped != NULL) {
955 g_hash_table_destroy(stopped);
956 stopped = NULL;
957 }
958
959 if (list == NULL) {
960 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
961 * If we've not probed for them yet, the Stopped list will be empty
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,
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
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 /* If there are no instances of this clone (perhaps because there are no
1020 * nodes configured), simply output the clone header by itself. This can
1021 * come up in PCS testing.
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
1034void
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 /* There could be a saved unexpanded xml */
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
1068enum rsc_role_e
1069clone_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
1094bool
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
1109gboolean
1110pe__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
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
1136const char *
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}
pe_resource_t * pe__create_clone_child(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: clone.c:153
int pe__clone_xml(pcmk__output_t *out, va_list args)
Definition: clone.c:695
bool pe__is_universal_clone(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: clone.c:1095
void pe__force_anon(const char *standard, pe_resource_t *rsc, const char *rid, pe_working_set_t *data_set)
Definition: clone.c:116
void clone_print(pe_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: clone.c:479
bool is_set_recursive(pe_resource_t *rsc, long long flag, bool any)
Definition: clone.c:448
gboolean clone_active(pe_resource_t *rsc, gboolean all)
Definition: clone.c:346
enum rsc_role_e clone_resource_state(const pe_resource_t *rsc, gboolean current)
Definition: clone.c:1069
void clone_free(pe_resource_t *rsc)
Definition: clone.c:1035
int pe__clone_default(pcmk__output_t *out, va_list args)
Definition: clone.c:757
gboolean pe__clone_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: clone.c:1110
const char * pe__clone_child_id(pe_resource_t *rsc)
Definition: clone.c:1137
gboolean clone_unpack(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: clone.c:205
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition: clone.c:135
#define PROMOTED_INSTANCES
Definition: clone.c:29
#define UNPROMOTED_INSTANCES
Definition: clone.c:30
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
gboolean crm_is_true(const char *s)
Definition: strings.c:416
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:122
@ pe_print_ncurses
Definition: common.h:122
@ pe_print_printf
Definition: common.h:123
@ pe_print_log
Definition: common.h:120
@ pe_print_xml
Definition: common.h:130
@ pe_print_clone_active
Definition: common.h:134
@ pe_print_pending
Definition: common.h:132
@ pe_print_suppres_nl
Definition: common.h:129
@ pe_print_clone_details
Definition: common.h:133
@ pe_print_html
Definition: common.h:121
const char * role2text(enum rsc_role_e role)
Definition: common.c:454
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
@ RSC_ROLE_STOPPED
Definition: common.h:94
@ RSC_ROLE_UNKNOWN
Definition: common.h:93
@ RSC_ROLE_UNPROMOTED
Definition: common.h:96
enum rsc_role_e text2role(const char *role)
Definition: common.c:483
pcmk__cpg_host_t host
Definition: cpg.c:4
enum crm_ais_msg_types type
Definition: cpg.c:3
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
#define pcmk__config_err(fmt...)
#define ID(x)
Definition: msg_xml.h:460
#define XML_RSC_ATTR_TARGET_ROLE
Definition: msg_xml.h:239
#define XML_RSC_ATTR_PROMOTED_MAX
Definition: msg_xml.h:236
#define XML_RSC_ATTR_STICKINESS
Definition: msg_xml.h:242
#define XML_ATTR_ID
Definition: msg_xml.h:135
#define XML_RSC_ATTR_INCARNATION_MAX
Definition: msg_xml.h:232
#define XML_RSC_ATTR_INCARNATION
Definition: msg_xml.h:231
#define PCMK_XE_PROMOTED_NODE_MAX_LEGACY
Definition: msg_xml.h:50
#define XML_RSC_ATTR_PROMOTED_NODEMAX
Definition: msg_xml.h:237
#define PCMK_XE_PROMOTED_MAX_LEGACY
Definition: msg_xml.h:49
#define XML_CIB_TAG_GROUP
Definition: msg_xml.h:221
#define XML_RSC_ATTR_ORDERED
Definition: msg_xml.h:229
#define XML_RSC_ATTR_UNIQUE
Definition: msg_xml.h:240
#define XML_LRM_ATTR_RC
Definition: msg_xml.h:314
#define XML_RSC_ATTR_INCARNATION_NODEMAX
Definition: msg_xml.h:234
#define XML_CIB_TAG_RESOURCE
Definition: msg_xml.h:220
pe_working_set_t * data_set
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
Control output from tools.
@ pcmk_show_pending
Definition: output.h:65
@ pcmk_show_clone_detail
Definition: output.h:59
@ pcmk_show_inactive_rscs
Definition: output.h:63
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:512
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
#define PCMK__OUTPUT_ARGS(ARGS...)
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
#define status_print(fmt, args...)
#define pe_rsc_managed
Definition: pe_types.h:257
#define pe_rsc_unique
Definition: pe_types.h:262
#define pe_rsc_orphan
Definition: pe_types.h:256
#define pe_rsc_failed
Definition: pe_types.h:276
#define pe_rsc_failure_ignored
Definition: pe_types.h:289
#define pe_rsc_promotable
Definition: pe_types.h:264
int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name, size_t pairs_count,...)
Definition: pe_output.c:496
void pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
Definition: utils.c:2039
bool pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node)
Definition: utils.c:2479
bool pe__resource_is_disabled(pe_resource_t *rsc)
Definition: utils.c:2425
#define pe_warn(fmt...)
Definition: internal.h:29
gint sort_node_uname(gconstpointer a, gconstpointer b)
Definition: utils.c:217
gboolean common_unpack(xmlNode *xml_obj, pe_resource_t **rsc, pe_resource_t *parent, pe_working_set_t *data_set)
Definition: complex.c:493
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:22
xmlNode * pe__failed_probe_for_rsc(pe_resource_t *rsc, const char *name)
Definition: utils.c:2570
void common_free(pe_resource_t *rsc)
Definition: complex.c:927
#define pe_err(fmt...)
Definition: internal.h:24
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition: common.c:504
#define CRM_ASSERT(expr)
Definition: results.h:42
@ pcmk_rc_no_output
Definition: results.h:116
@ pcmk_rc_ok
Definition: results.h:146
Cluster status and scheduling.
pe_node_t * pe_find_node(GList *node_list, const char *uname)
Definition: status.c:443
const char * rsc_printable_id(pe_resource_t *rsc)
Definition: utils.c:2012
pe_resource_t * pe_find_resource(GList *rsc_list, const char *id_rh)
Definition: status.c:391
void pcmk__add_separated_word(char **list, size_t *len, const char *word, const char *separator)
Definition: strings.c:703
int pcmk__numeric_strcasecmp(const char *s1, const char *s2)
Definition: strings.c:1021
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
gboolean pcmk__str_in_list(const gchar *s, GList *lst, uint32_t flags)
Definition: strings.c:886
@ pcmk__str_star_matches
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:955
This structure contains everything that makes up a single output formatter.
struct pe_node_shared_s * details
Definition: pe_types.h:252
gboolean online
Definition: pe_types.h:220
const char * uname
Definition: pe_types.h:216
gboolean unclean
Definition: pe_types.h:224
GList * running_on
Definition: pe_types.h:380
GHashTable * meta
Definition: pe_types.h:387
GList * children
Definition: pe_types.h:391
GHashTable * known_on
Definition: pe_types.h:381
char * id
Definition: pe_types.h:336
xmlNode * xml
Definition: pe_types.h:338
GHashTable * allowed_nodes
Definition: pe_types.h:382
void * variant_opaque
Definition: pe_types.h:346
unsigned long long flags
Definition: pe_types.h:362
char * pending_task
Definition: pe_types.h:360
xmlNode * orig_xml
Definition: pe_types.h:339
resource_object_functions_t * fns
Definition: pe_types.h:347
GList * nodes
Definition: pe_types.h:164
gboolean(* is_filtered)(pe_resource_t *, GList *, gboolean)
Definition: pe_types.h:58
gboolean(* active)(pe_resource_t *, gboolean)
Definition: pe_types.h:53
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition: pe_types.h:55
enum rsc_role_e(* state)(const pe_resource_t *, gboolean)
Definition: pe_types.h:54
void(* free)(pe_resource_t *)
Definition: pe_types.h:56
void(* print)(pe_resource_t *, const char *, long, void *)
Definition: pe_types.h:52
void free_xml(xmlNode *child)
Definition: xml.c:824
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:830