pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
clone.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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 <crm/pengine/rules.h>
13#include <crm/pengine/status.h>
15#include <pe_status_private.h>
16#include <crm/msg_xml.h>
17#include <crm/common/output.h>
19
20#define VARIANT_CLONE 1
21#include "./variant.h"
22
23#ifdef PCMK__COMPAT_2_0
24#define PROMOTED_INSTANCES RSC_ROLE_PROMOTED_LEGACY_S "s"
25#define UNPROMOTED_INSTANCES RSC_ROLE_UNPROMOTED_LEGACY_S "s"
26#else
27#define PROMOTED_INSTANCES RSC_ROLE_PROMOTED_S
28#define UNPROMOTED_INSTANCES RSC_ROLE_UNPROMOTED_S
29#endif
30
31static void
32clone_header(pcmk__output_t *out, int *rc, pe_resource_t *rsc, clone_variant_data_t *clone_data)
33{
34 char *attrs = NULL;
35 size_t len = 0;
36
38 pcmk__add_separated_word(&attrs, &len, "promotable", ", ");
39 }
40
41 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
42 pcmk__add_separated_word(&attrs, &len, "unique", ", ");
43 }
44
45 if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
46 pcmk__add_separated_word(&attrs, &len, "unmanaged", ", ");
47 }
48
49 if (pe__resource_is_disabled(rsc)) {
50 pcmk__add_separated_word(&attrs, &len, "disabled", ", ");
51 }
52
53 if (attrs) {
54 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)",
55 rsc->id, ID(clone_data->xml_obj_child),
56 attrs);
57 free(attrs);
58 } else {
59 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]",
60 rsc->id, ID(clone_data->xml_obj_child))
61 }
62}
63
64void
65pe__force_anon(const char *standard, pe_resource_t *rsc, const char *rid,
66 pe_working_set_t *data_set)
67{
68 if (pe_rsc_is_clone(rsc)) {
69 clone_variant_data_t *clone_data = NULL;
70
71 get_clone_variant_data(clone_data, rsc);
72
73 pe_warn("Ignoring " XML_RSC_ATTR_UNIQUE " for %s because %s resources "
74 "such as %s can be used only as anonymous clones",
75 rsc->id, standard, rid);
76
77 clone_data->clone_node_max = 1;
78 clone_data->clone_max = QB_MIN(clone_data->clone_max,
79 g_list_length(data_set->nodes));
80 }
81}
82
84find_clone_instance(pe_resource_t * rsc, const char *sub_id, pe_working_set_t * data_set)
85{
86 char *child_id = NULL;
87 pe_resource_t *child = NULL;
88 const char *child_base = NULL;
89 clone_variant_data_t *clone_data = NULL;
90
91 get_clone_variant_data(clone_data, rsc);
92
93 child_base = ID(clone_data->xml_obj_child);
94 child_id = crm_strdup_printf("%s:%s", child_base, sub_id);
95 child = pe_find_resource(rsc->children, child_id);
96
97 free(child_id);
98 return child;
99}
100
103{
104 gboolean as_orphan = FALSE;
105 char *inc_num = NULL;
106 char *inc_max = NULL;
107 pe_resource_t *child_rsc = NULL;
108 xmlNode *child_copy = NULL;
109 clone_variant_data_t *clone_data = NULL;
110
111 get_clone_variant_data(clone_data, rsc);
112
113 CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
114
115 if (clone_data->total_clones >= clone_data->clone_max) {
116 // If we've already used all available instances, this is an orphan
117 as_orphan = TRUE;
118 }
119
120 // Allocate instance numbers in numerical order (starting at 0)
121 inc_num = pcmk__itoa(clone_data->total_clones);
122 inc_max = pcmk__itoa(clone_data->clone_max);
123
124 child_copy = copy_xml(clone_data->xml_obj_child);
125
126 crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num);
127
128 if (common_unpack(child_copy, &child_rsc, rsc, data_set) == FALSE) {
129 pe_err("Failed unpacking resource %s", crm_element_value(child_copy, XML_ATTR_ID));
130 child_rsc = NULL;
131 goto bail;
132 }
133/* child_rsc->globally_unique = rsc->globally_unique; */
134
135 CRM_ASSERT(child_rsc);
136 clone_data->total_clones += 1;
137 pe_rsc_trace(child_rsc, "Setting clone attributes for: %s", child_rsc->id);
138 rsc->children = g_list_append(rsc->children, child_rsc);
139 if (as_orphan) {
141 }
142
144 pe_rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
145
146 bail:
147 free(inc_num);
148 free(inc_max);
149
150 return child_rsc;
151}
152
153gboolean
155{
156 int lpc = 0;
157 xmlNode *a_child = NULL;
158 xmlNode *xml_obj = rsc->xml;
159 clone_variant_data_t *clone_data = NULL;
160
161 const char *ordered = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_ORDERED);
162 const char *max_clones = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_MAX);
163 const char *max_clones_node = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX);
164
165 pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
166
167 clone_data = calloc(1, sizeof(clone_variant_data_t));
168 rsc->variant_opaque = clone_data;
169
171 const char *promoted_max = NULL;
172 const char *promoted_node_max = NULL;
173
174 promoted_max = g_hash_table_lookup(rsc->meta,
176 if (promoted_max == NULL) {
177 // @COMPAT deprecated since 2.0.0
178 promoted_max = g_hash_table_lookup(rsc->meta,
180 }
181
182 promoted_node_max = g_hash_table_lookup(rsc->meta,
184 if (promoted_node_max == NULL) {
185 // @COMPAT deprecated since 2.0.0
186 promoted_node_max = g_hash_table_lookup(rsc->meta,
188 }
189
190 // Use 1 as default but 0 for minimum and invalid
191 if (promoted_max == NULL) {
192 clone_data->promoted_max = 1;
193 } else {
194 pcmk__scan_min_int(promoted_max, &(clone_data->promoted_max), 0);
195 }
196
197 // Use 1 as default but 0 for minimum and invalid
198 if (promoted_node_max == NULL) {
199 clone_data->promoted_node_max = 1;
200 } else {
201 pcmk__scan_min_int(promoted_node_max,
202 &(clone_data->promoted_node_max), 0);
203 }
204 }
205
206 // Implied by calloc()
207 /* clone_data->xml_obj_child = NULL; */
208
209 // Use 1 as default but 0 for minimum and invalid
210 if (max_clones_node == NULL) {
211 clone_data->clone_node_max = 1;
212 } else {
213 pcmk__scan_min_int(max_clones_node, &(clone_data->clone_node_max), 0);
214 }
215
216 /* Use number of nodes (but always at least 1, which is handy for crm_verify
217 * for a CIB without nodes) as default, but 0 for minimum and invalid
218 */
219 if (max_clones == NULL) {
220 clone_data->clone_max = QB_MAX(1, g_list_length(data_set->nodes));
221 } else {
222 pcmk__scan_min_int(max_clones, &(clone_data->clone_max), 0);
223 }
224
225 clone_data->ordered = crm_is_true(ordered);
226
227 if ((rsc->flags & pe_rsc_unique) == 0 && clone_data->clone_node_max > 1) {
228 pcmk__config_err("Ignoring " XML_RSC_ATTR_PROMOTED_MAX " for %s "
229 "because anonymous clones support only one instance "
230 "per node", rsc->id);
231 clone_data->clone_node_max = 1;
232 }
233
234 pe_rsc_trace(rsc, "Options for %s", rsc->id);
235 pe_rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
236 pe_rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
237 pe_rsc_trace(rsc, "\tClone is unique: %s",
238 pe__rsc_bool_str(rsc, pe_rsc_unique));
239 pe_rsc_trace(rsc, "\tClone is promotable: %s",
240 pe__rsc_bool_str(rsc, pe_rsc_promotable));
241
242 // Clones may contain a single group or primitive
243 for (a_child = pcmk__xe_first_child(xml_obj); a_child != NULL;
244 a_child = pcmk__xe_next(a_child)) {
245
246 if (pcmk__str_any_of((const char *)a_child->name, XML_CIB_TAG_RESOURCE, XML_CIB_TAG_GROUP, NULL)) {
247 clone_data->xml_obj_child = a_child;
248 break;
249 }
250 }
251
252 if (clone_data->xml_obj_child == NULL) {
253 pcmk__config_err("%s has nothing to clone", rsc->id);
254 return FALSE;
255 }
256
257 /*
258 * Make clones ever so slightly sticky by default
259 *
260 * This helps ensure clone instances are not shuffled around the cluster
261 * for no benefit in situations when pre-allocation is not appropriate
262 */
263 if (g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_STICKINESS) == NULL) {
265 }
266
267 /* This ensures that the globally-unique value always exists for children to
268 * inherit when being unpacked, as well as in resource agents' environment.
269 */
271 pe__rsc_bool_str(rsc, pe_rsc_unique));
272
273 if (clone_data->clone_max <= 0) {
274 /* Create one child instance so that unpack_find_resource() will hook up
275 * any orphans up to the parent correctly.
276 */
277 if (pe__create_clone_child(rsc, data_set) == NULL) {
278 return FALSE;
279 }
280
281 } else {
282 // Create a child instance for each available instance number
283 for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
284 if (pe__create_clone_child(rsc, data_set) == NULL) {
285 return FALSE;
286 }
287 }
288 }
289
290 pe_rsc_trace(rsc, "Added %d children to resource %s...", clone_data->clone_max, rsc->id);
291 return TRUE;
292}
293
294gboolean
295clone_active(pe_resource_t * rsc, gboolean all)
296{
297 GList *gIter = rsc->children;
298
299 for (; gIter != NULL; gIter = gIter->next) {
300 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
301 gboolean child_active = child_rsc->fns->active(child_rsc, all);
302
303 if (all == FALSE && child_active) {
304 return TRUE;
305 } else if (all && child_active == FALSE) {
306 return FALSE;
307 }
308 }
309
310 if (all) {
311 return TRUE;
312 } else {
313 return FALSE;
314 }
315}
316
317static void
318short_print(char *list, const char *prefix, const char *type, const char *suffix, long options, void *print_data)
319{
320 if(suffix == NULL) {
321 suffix = "";
322 }
323
324 if (list) {
325 if (options & pe_print_html) {
326 status_print("<li>");
327 }
328 status_print("%s%s: [ %s ]%s", prefix, type, list, suffix);
329
330 if (options & pe_print_html) {
331 status_print("</li>\n");
332
333 } else if (options & pe_print_suppres_nl) {
334 /* nothing */
335 } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
336 status_print("\n");
337 }
338
339 }
340}
341
342static const char *
343configured_role_str(pe_resource_t * rsc)
344{
345 const char *target_role = g_hash_table_lookup(rsc->meta,
347
348 if ((target_role == NULL) && rsc->children && rsc->children->data) {
349 target_role = g_hash_table_lookup(((pe_resource_t*)rsc->children->data)->meta,
351 }
352 return target_role;
353}
354
355static enum rsc_role_e
356configured_role(pe_resource_t * rsc)
357{
358 const char *target_role = configured_role_str(rsc);
359
360 if (target_role) {
361 return text2role(target_role);
362 }
363 return RSC_ROLE_UNKNOWN;
364}
365
366static void
367clone_print_xml(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
368{
369 char *child_text = crm_strdup_printf("%s ", pre_text);
370 const char *target_role = configured_role_str(rsc);
371 GList *gIter = rsc->children;
372
373 status_print("%s<clone ", pre_text);
374 status_print("id=\"%s\" ", rsc->id);
375 status_print("multi_state=\"%s\" ",
376 pe__rsc_bool_str(rsc, pe_rsc_promotable));
377 status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_unique));
378 status_print("managed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_managed));
379 status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_failed));
380 status_print("failure_ignored=\"%s\" ",
381 pe__rsc_bool_str(rsc, pe_rsc_failure_ignored));
382 if (target_role) {
383 status_print("target_role=\"%s\" ", target_role);
384 }
385 status_print(">\n");
386
387 for (; gIter != NULL; gIter = gIter->next) {
388 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
389
390 child_rsc->fns->print(child_rsc, child_text, options, print_data);
391 }
392
393 status_print("%s</clone>\n", pre_text);
394 free(child_text);
395}
396
397bool is_set_recursive(pe_resource_t * rsc, long long flag, bool any)
398{
399 GList *gIter;
400 bool all = !any;
401
402 if (pcmk_is_set(rsc->flags, flag)) {
403 if(any) {
404 return TRUE;
405 }
406 } else if(all) {
407 return FALSE;
408 }
409
410 for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
411 if(is_set_recursive(gIter->data, flag, any)) {
412 if(any) {
413 return TRUE;
414 }
415
416 } else if(all) {
417 return FALSE;
418 }
419 }
420
421 if(all) {
422 return TRUE;
423 }
424 return FALSE;
425}
426
427void
428clone_print(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
429{
430 char *list_text = NULL;
431 char *child_text = NULL;
432 char *stopped_list = NULL;
433 size_t list_text_len = 0;
434 size_t stopped_list_len = 0;
435
436 GList *promoted_list = NULL;
437 GList *started_list = NULL;
438 GList *gIter = rsc->children;
439
440 clone_variant_data_t *clone_data = NULL;
441 int active_instances = 0;
442
443 if (pre_text == NULL) {
444 pre_text = " ";
445 }
446
447 if (options & pe_print_xml) {
448 clone_print_xml(rsc, pre_text, options, print_data);
449 return;
450 }
451
452 get_clone_variant_data(clone_data, rsc);
453
454 child_text = crm_strdup_printf("%s ", pre_text);
455
456 status_print("%sClone Set: %s [%s]%s%s%s",
457 pre_text ? pre_text : "", rsc->id, ID(clone_data->xml_obj_child),
458 pcmk_is_set(rsc->flags, pe_rsc_promotable)? " (promotable)" : "",
459 pcmk_is_set(rsc->flags, pe_rsc_unique)? " (unique)" : "",
460 pcmk_is_set(rsc->flags, pe_rsc_managed)? "" : " (unmanaged)");
461
462 if (options & pe_print_html) {
463 status_print("\n<ul>\n");
464
465 } else if ((options & pe_print_log) == 0) {
466 status_print("\n");
467 }
468
469 for (; gIter != NULL; gIter = gIter->next) {
470 gboolean print_full = FALSE;
471 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
472 gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
473
474 if (options & pe_print_clone_details) {
475 print_full = TRUE;
476 }
477
478 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
479 // Print individual instance when unique (except stopped orphans)
480 if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
481 print_full = TRUE;
482 }
483
484 // Everything else in this block is for anonymous clones
485
486 } else if (pcmk_is_set(options, pe_print_pending)
487 && (child_rsc->pending_task != NULL)
488 && strcmp(child_rsc->pending_task, "probe")) {
489 // Print individual instance when non-probe action is pending
490 print_full = TRUE;
491
492 } else if (partially_active == FALSE) {
493 // List stopped instances when requested (except orphans)
494 if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
495 && !pcmk_is_set(options, pe_print_clone_active)) {
496 pcmk__add_word(&stopped_list, &stopped_list_len, child_rsc->id);
497 }
498
499 } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
500 || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
501 || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
502
503 // Print individual instance when active orphaned/unmanaged/failed
504 print_full = TRUE;
505
506 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
507 // Instance of fully active anonymous clone
508
509 pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
510
511 if (location) {
512 // Instance is active on a single node
513
514 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
515
516 if (location->details->online == FALSE && location->details->unclean) {
517 print_full = TRUE;
518
519 } else if (a_role > RSC_ROLE_UNPROMOTED) {
520 promoted_list = g_list_append(promoted_list, location);
521
522 } else {
523 started_list = g_list_append(started_list, location);
524 }
525
526 } else {
527 /* uncolocated group - bleh */
528 print_full = TRUE;
529 }
530
531 } else {
532 // Instance of partially active anonymous clone
533 print_full = TRUE;
534 }
535
536 if (print_full) {
537 if (options & pe_print_html) {
538 status_print("<li>\n");
539 }
540 child_rsc->fns->print(child_rsc, child_text, options, print_data);
541 if (options & pe_print_html) {
542 status_print("</li>\n");
543 }
544 }
545 }
546
547 /* Promoted */
548 promoted_list = g_list_sort(promoted_list, sort_node_uname);
549 for (gIter = promoted_list; gIter; gIter = gIter->next) {
550 pe_node_t *host = gIter->data;
551
552 pcmk__add_word(&list_text, &list_text_len, host->details->uname);
553 active_instances++;
554 }
555
556 short_print(list_text, child_text, PROMOTED_INSTANCES, NULL, options,
557 print_data);
558 g_list_free(promoted_list);
559 free(list_text);
560 list_text = NULL;
561 list_text_len = 0;
562
563 /* Started/Unpromoted */
564 started_list = g_list_sort(started_list, sort_node_uname);
565 for (gIter = started_list; gIter; gIter = gIter->next) {
566 pe_node_t *host = gIter->data;
567
568 pcmk__add_word(&list_text, &list_text_len, host->details->uname);
569 active_instances++;
570 }
571
573 enum rsc_role_e role = configured_role(rsc);
574
575 if (role == RSC_ROLE_UNPROMOTED) {
576 short_print(list_text, child_text,
577 UNPROMOTED_INSTANCES " (target-role)", NULL, options,
578 print_data);
579 } else {
580 short_print(list_text, child_text, UNPROMOTED_INSTANCES, NULL,
581 options, print_data);
582 }
583
584 } else {
585 short_print(list_text, child_text, "Started", NULL, options, print_data);
586 }
587
588 g_list_free(started_list);
589 free(list_text);
590 list_text = NULL;
591 list_text_len = 0;
592
593 if (!pcmk_is_set(options, pe_print_clone_active)) {
594 const char *state = "Stopped";
595 enum rsc_role_e role = configured_role(rsc);
596
597 if (role == RSC_ROLE_STOPPED) {
598 state = "Stopped (disabled)";
599 }
600
602 && (clone_data->clone_max > active_instances)) {
603
604 GList *nIter;
605 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
606
607 /* Custom stopped list for non-unique clones */
608 free(stopped_list);
609 stopped_list = NULL;
610 stopped_list_len = 0;
611
612 if (list == NULL) {
613 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
614 * If we've not probed for them yet, the Stopped list will be empty
615 */
616 list = g_hash_table_get_values(rsc->known_on);
617 }
618
619 list = g_list_sort(list, sort_node_uname);
620 for (nIter = list; nIter != NULL; nIter = nIter->next) {
621 pe_node_t *node = (pe_node_t *)nIter->data;
622
623 if (pe_find_node(rsc->running_on, node->details->uname) == NULL) {
624 pcmk__add_word(&stopped_list, &stopped_list_len,
625 node->details->uname);
626 }
627 }
628 g_list_free(list);
629 }
630
631 short_print(stopped_list, child_text, state, NULL, options, print_data);
632 free(stopped_list);
633 }
634
635 if (options & pe_print_html) {
636 status_print("</ul>\n");
637 }
638
639 free(child_text);
640}
641
642PCMK__OUTPUT_ARGS("clone", "unsigned int", "pe_resource_t *", "GList *", "GList *")
643int
644pe__clone_xml(pcmk__output_t *out, va_list args)
645{
646 unsigned int show_opts = va_arg(args, unsigned int);
647 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
648 GList *only_node = va_arg(args, GList *);
649 GList *only_rsc = va_arg(args, GList *);
650
651 GList *gIter = rsc->children;
652 GList *all = NULL;
653 int rc = pcmk_rc_no_output;
654 gboolean printed_header = FALSE;
655 gboolean print_everything = TRUE;
656
657 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
658 return rc;
659 }
660
661 print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) ||
662 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none));
663
664 all = g_list_prepend(all, (gpointer) "*");
665
666 for (; gIter != NULL; gIter = gIter->next) {
667 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
668
669 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
670 continue;
671 }
672
673 if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
674 continue;
675 }
676
677 if (!printed_header) {
678 printed_header = TRUE;
679
680 rc = pe__name_and_nvpairs_xml(out, true, "clone", 8,
681 "id", rsc->id,
682 "multi_state", pe__rsc_bool_str(rsc, pe_rsc_promotable),
683 "unique", pe__rsc_bool_str(rsc, pe_rsc_unique),
684 "managed", pe__rsc_bool_str(rsc, pe_rsc_managed),
685 "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)),
686 "failed", pe__rsc_bool_str(rsc, pe_rsc_failed),
687 "failure_ignored", pe__rsc_bool_str(rsc, pe_rsc_failure_ignored),
688 "target_role", configured_role_str(rsc));
690 }
691
692 out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
693 child_rsc, only_node, all);
694 }
695
696 if (printed_header) {
698 }
699
700 g_list_free(all);
701 return rc;
702}
703
704PCMK__OUTPUT_ARGS("clone", "unsigned int", "pe_resource_t *", "GList *", "GList *")
705int
707{
708 unsigned int show_opts = va_arg(args, unsigned int);
709 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
710 GList *only_node = va_arg(args, GList *);
711 GList *only_rsc = va_arg(args, GList *);
712
713 char *list_text = NULL;
714 char *stopped_list = NULL;
715 size_t list_text_len = 0;
716 size_t stopped_list_len = 0;
717
718 GList *promoted_list = NULL;
719 GList *started_list = NULL;
720 GList *gIter = rsc->children;
721
722 clone_variant_data_t *clone_data = NULL;
723 int active_instances = 0;
724 int rc = pcmk_rc_no_output;
725 gboolean print_everything = TRUE;
726
727 get_clone_variant_data(clone_data, rsc);
728
729 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
730 return rc;
731 }
732
733 print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) ||
734 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none));
735
736 for (; gIter != NULL; gIter = gIter->next) {
737 gboolean print_full = FALSE;
738 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
739 gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
740
741 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
742 continue;
743 }
744
745 if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
746 continue;
747 }
748
749 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
750 print_full = TRUE;
751 }
752
753 if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
754 // Print individual instance when unique (except stopped orphans)
755 if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
756 print_full = TRUE;
757 }
758
759 // Everything else in this block is for anonymous clones
760
761 } else if (pcmk_is_set(show_opts, pcmk_show_pending)
762 && (child_rsc->pending_task != NULL)
763 && strcmp(child_rsc->pending_task, "probe")) {
764 // Print individual instance when non-probe action is pending
765 print_full = TRUE;
766
767 } else if (partially_active == FALSE) {
768 // List stopped instances when requested (except orphans)
769 if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
770 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
771 pcmk__add_word(&stopped_list, &stopped_list_len, child_rsc->id);
772 }
773
774 } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
775 || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
776 || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
777
778 // Print individual instance when active orphaned/unmanaged/failed
779 print_full = TRUE;
780
781 } else if (child_rsc->fns->active(child_rsc, TRUE)) {
782 // Instance of fully active anonymous clone
783
784 pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
785
786 if (location) {
787 // Instance is active on a single node
788
789 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
790
791 if (location->details->online == FALSE && location->details->unclean) {
792 print_full = TRUE;
793
794 } else if (a_role > RSC_ROLE_UNPROMOTED) {
795 promoted_list = g_list_append(promoted_list, location);
796
797 } else {
798 started_list = g_list_append(started_list, location);
799 }
800
801 } else {
802 /* uncolocated group - bleh */
803 print_full = TRUE;
804 }
805
806 } else {
807 // Instance of partially active anonymous clone
808 print_full = TRUE;
809 }
810
811 if (print_full) {
812 GList *all = NULL;
813
814 clone_header(out, &rc, rsc, clone_data);
815
816 /* Print every resource that's a child of this clone. */
817 all = g_list_prepend(all, (gpointer) "*");
818 out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
819 child_rsc, only_node, all);
820 g_list_free(all);
821 }
822 }
823
824 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
825 free(stopped_list);
827 return pcmk_rc_ok;
828 }
829
830 /* Promoted */
831 promoted_list = g_list_sort(promoted_list, sort_node_uname);
832 for (gIter = promoted_list; gIter; gIter = gIter->next) {
833 pe_node_t *host = gIter->data;
834
835 if (!pcmk__str_in_list(only_node, host->details->uname, pcmk__str_casei)) {
836 continue;
837 }
838
839 pcmk__add_word(&list_text, &list_text_len, host->details->uname);
840 active_instances++;
841 }
842 g_list_free(promoted_list);
843
844 if (list_text != NULL) {
845 clone_header(out, &rc, rsc, clone_data);
846
847 out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]", list_text);
848 free(list_text);
849 list_text = NULL;
850 list_text_len = 0;
851 }
852
853 /* Started/Unpromoted */
854 started_list = g_list_sort(started_list, sort_node_uname);
855 for (gIter = started_list; gIter; gIter = gIter->next) {
856 pe_node_t *host = gIter->data;
857
858 if (!pcmk__str_in_list(only_node, host->details->uname, pcmk__str_casei)) {
859 continue;
860 }
861
862 pcmk__add_word(&list_text, &list_text_len, host->details->uname);
863 active_instances++;
864 }
865 g_list_free(started_list);
866
867 if (list_text != NULL) {
868 clone_header(out, &rc, rsc, clone_data);
869
871 enum rsc_role_e role = configured_role(rsc);
872
873 if (role == RSC_ROLE_UNPROMOTED) {
874 out->list_item(out, NULL,
875 UNPROMOTED_INSTANCES " (target-role): [ %s ]",
876 list_text);
877 } else {
878 out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
879 list_text);
880 }
881
882 } else {
883 out->list_item(out, NULL, "Started: [ %s ]", list_text);
884 }
885 free(list_text);
886 list_text = NULL;
887 list_text_len = 0;
888 }
889
890 if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
891 const char *state = "Stopped";
892 enum rsc_role_e role = configured_role(rsc);
893
894 if (role == RSC_ROLE_STOPPED) {
895 state = "Stopped (disabled)";
896 }
897
899 && (clone_data->clone_max > active_instances)) {
900
901 GList *nIter;
902 GList *list = g_hash_table_get_values(rsc->allowed_nodes);
903
904 /* Custom stopped list for non-unique clones */
905 free(stopped_list);
906 stopped_list = NULL;
907 stopped_list_len = 0;
908
909 if (list == NULL) {
910 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
911 * If we've not probed for them yet, the Stopped list will be empty
912 */
913 list = g_hash_table_get_values(rsc->known_on);
914 }
915
916 list = g_list_sort(list, sort_node_uname);
917 for (nIter = list; nIter != NULL; nIter = nIter->next) {
918 pe_node_t *node = (pe_node_t *)nIter->data;
919
920 if (pe_find_node(rsc->running_on, node->details->uname) == NULL &&
921 pcmk__str_in_list(only_node, node->details->uname, pcmk__str_casei)) {
922 pcmk__add_word(&stopped_list, &stopped_list_len,
923 node->details->uname);
924 }
925 }
926 g_list_free(list);
927 }
928
929 if (stopped_list != NULL) {
930 clone_header(out, &rc, rsc, clone_data);
931
932 out->list_item(out, NULL, "%s: [ %s ]", state, stopped_list);
933 free(stopped_list);
934 stopped_list_len = 0;
935
936 /* If there are no instances of this clone (perhaps because there are no
937 * nodes configured), simply output the clone header by itself. This can
938 * come up in PCS testing.
939 */
940 } else if (active_instances == 0) {
941 clone_header(out, &rc, rsc, clone_data);
943 return rc;
944 }
945 }
946
948 return rc;
949}
950
951void
953{
954 clone_variant_data_t *clone_data = NULL;
955
956 get_clone_variant_data(clone_data, rsc);
957
958 pe_rsc_trace(rsc, "Freeing %s", rsc->id);
959
960 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
961 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
962
963 CRM_ASSERT(child_rsc);
964 pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
965 free_xml(child_rsc->xml);
966 child_rsc->xml = NULL;
967 /* There could be a saved unexpanded xml */
968 free_xml(child_rsc->orig_xml);
969 child_rsc->orig_xml = NULL;
970 child_rsc->fns->free(child_rsc);
971 }
972
973 g_list_free(rsc->children);
974
975 if (clone_data) {
976 CRM_ASSERT(clone_data->demote_notify == NULL);
977 CRM_ASSERT(clone_data->stop_notify == NULL);
978 CRM_ASSERT(clone_data->start_notify == NULL);
979 CRM_ASSERT(clone_data->promote_notify == NULL);
980 }
981
982 common_free(rsc);
983}
984
985enum rsc_role_e
986clone_resource_state(const pe_resource_t * rsc, gboolean current)
987{
988 enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN;
989 GList *gIter = rsc->children;
990
991 for (; gIter != NULL; gIter = gIter->next) {
992 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
993 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
994
995 if (a_role > clone_role) {
996 clone_role = a_role;
997 }
998 }
999
1000 pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(clone_role));
1001 return clone_role;
1002}
1003
1011bool
1013 pe_working_set_t *data_set)
1014{
1015 if (pe_rsc_is_clone(rsc)) {
1016 clone_variant_data_t *clone_data = NULL;
1017
1018 get_clone_variant_data(clone_data, rsc);
1019 if (clone_data->clone_max == g_list_length(data_set->nodes)) {
1020 return TRUE;
1021 }
1022 }
1023 return FALSE;
1024}
1025
1026gboolean
1027pe__clone_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
1028{
1029 gboolean passes = FALSE;
1030 clone_variant_data_t *clone_data = NULL;
1031
1032 if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none)) {
1033 passes = TRUE;
1034 } else {
1035 get_clone_variant_data(clone_data, rsc);
1036 passes = pcmk__str_in_list(only_rsc, ID(clone_data->xml_obj_child), pcmk__str_none);
1037
1038 if (!passes) {
1039 for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1040 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1041
1042 if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
1043 passes = TRUE;
1044 break;
1045 }
1046 }
1047 }
1048 }
1049
1050 return !passes;
1051}
pe_resource_t * pe__create_clone_child(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition clone.c:102
int pe__clone_xml(pcmk__output_t *out, va_list args)
Definition clone.c:644
bool pe__is_universal_clone(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition clone.c:1012
void pe__force_anon(const char *standard, pe_resource_t *rsc, const char *rid, pe_working_set_t *data_set)
Definition clone.c:65
void clone_print(pe_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition clone.c:428
bool is_set_recursive(pe_resource_t *rsc, long long flag, bool any)
Definition clone.c:397
gboolean clone_active(pe_resource_t *rsc, gboolean all)
Definition clone.c:295
enum rsc_role_e clone_resource_state(const pe_resource_t *rsc, gboolean current)
Definition clone.c:986
void clone_free(pe_resource_t *rsc)
Definition clone.c:952
int pe__clone_default(pcmk__output_t *out, va_list args)
Definition clone.c:706
gboolean pe__clone_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition clone.c:1027
gboolean clone_unpack(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition clone.c:154
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition clone.c:84
#define PROMOTED_INSTANCES
Definition clone.c:27
#define UNPROMOTED_INSTANCES
Definition clone.c:28
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
gboolean crm_is_true(const char *s)
Definition strings.c:415
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:114
@ 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:459
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:488
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:218
#define pcmk__config_err(fmt...)
#define ID(x)
Definition msg_xml.h:456
#define XML_RSC_ATTR_TARGET_ROLE
Definition msg_xml.h:233
#define XML_RSC_ATTR_PROMOTED_MAX
Definition msg_xml.h:230
#define XML_RSC_ATTR_STICKINESS
Definition msg_xml.h:236
#define XML_ATTR_ID
Definition msg_xml.h:129
#define XML_RSC_ATTR_INCARNATION_MAX
Definition msg_xml.h:226
#define XML_RSC_ATTR_INCARNATION
Definition msg_xml.h:225
#define PCMK_XE_PROMOTED_NODE_MAX_LEGACY
Definition msg_xml.h:44
#define XML_RSC_ATTR_PROMOTED_NODEMAX
Definition msg_xml.h:231
#define PCMK_XE_PROMOTED_MAX_LEGACY
Definition msg_xml.h:43
#define XML_CIB_TAG_GROUP
Definition msg_xml.h:215
#define XML_RSC_ATTR_ORDERED
Definition msg_xml.h:223
#define XML_RSC_ATTR_UNIQUE
Definition msg_xml.h:234
#define XML_RSC_ATTR_INCARNATION_NODEMAX
Definition msg_xml.h:228
#define XML_CIB_TAG_RESOURCE
Definition msg_xml.h:214
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:530
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:324
Control output from tools.
@ pcmk_show_pending
Definition output.h:63
@ pcmk_show_clone_detail
Definition output.h:57
@ pcmk_show_inactive_rscs
Definition output.h:61
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition output_xml.c:511
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
#define PCMK__OUTPUT_ARGS(ARGS...)
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
int rc
Definition pcmk_fence.c:35
#define status_print(fmt, args...)
#define pe_rsc_managed
Definition pe_types.h:249
#define pe_rsc_unique
Definition pe_types.h:254
#define pe_rsc_orphan
Definition pe_types.h:248
#define pe_rsc_failed
Definition pe_types.h:267
#define pe_rsc_failure_ignored
Definition pe_types.h:275
#define pe_rsc_promotable
Definition pe_types.h:256
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:543
void pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
Definition utils.c:1944
bool pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node)
Definition utils.c:2412
bool pe__resource_is_disabled(pe_resource_t *rsc)
Definition utils.c:2359
#define pe_warn(fmt...)
Definition internal.h:27
gint sort_node_uname(gconstpointer a, gconstpointer b)
Definition utils.c:218
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:20
void common_free(pe_resource_t *rsc)
Definition complex.c:917
#define pe_err(fmt...)
Definition internal.h:22
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition common.c:579
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_no_output
Definition results.h:112
@ pcmk_rc_ok
Definition results.h:142
Cluster status and scheduling.
pe_node_t * pe_find_node(GList *node_list, const char *uname)
Definition status.c:434
const char * rsc_printable_id(pe_resource_t *rsc)
Definition utils.c:1917
pe_resource_t * pe_find_resource(GList *rsc_list, const char *id_rh)
Definition status.c:382
void pcmk__add_separated_word(char **list, size_t *len, const char *word, const char *separator)
Definition strings.c:702
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:127
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:979
gboolean pcmk__str_in_list(GList *lst, const gchar *s, uint32_t flags)
Definition strings.c:895
This structure contains everything that makes up a single output formatter.
struct pe_node_shared_s * details
Definition pe_types.h:244
gboolean online
Definition pe_types.h:213
const char * uname
Definition pe_types.h:209
gboolean unclean
Definition pe_types.h:217
GList * running_on
Definition pe_types.h:367
GHashTable * meta
Definition pe_types.h:374
GList * children
Definition pe_types.h:378
GHashTable * known_on
Definition pe_types.h:368
xmlNode * xml
Definition pe_types.h:324
GHashTable * allowed_nodes
Definition pe_types.h:369
void * variant_opaque
Definition pe_types.h:332
unsigned long long flags
Definition pe_types.h:349
char * pending_task
Definition pe_types.h:347
xmlNode * orig_xml
Definition pe_types.h:325
resource_object_functions_t * fns
Definition pe_types.h:333
gboolean(* is_filtered)(pe_resource_t *, GList *, gboolean)
Definition pe_types.h:57
gboolean(* active)(pe_resource_t *, gboolean)
Definition pe_types.h:52
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition pe_types.h:54
enum rsc_role_e(* state)(const pe_resource_t *, gboolean)
Definition pe_types.h:53
void(* free)(pe_resource_t *)
Definition pe_types.h:55
void(* print)(pe_resource_t *, const char *, long, void *)
Definition pe_types.h:51
void free_xml(xmlNode *child)
Definition xml.c:823
xmlNode * copy_xml(xmlNode *src_node)
Definition xml.c:829