pacemaker  2.1.1-52dc28db4
Scalable High-Availability cluster resource manager
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>
14 #include <crm/pengine/internal.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 
31 static void
32 clone_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 
37  if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
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 
64 void
65 pe__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 
84 find_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 
143  add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max);
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 
153 gboolean
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 
170  if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
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 
294 gboolean
295 clone_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 
317 static void
318 short_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 
342 static const char *
343 configured_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 
355 static enum rsc_role_e
356 configured_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 
366 static void
367 clone_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 
397 bool 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 
427 void
428 clone_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 
572  if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
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 
601  if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
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 
642 PCMK__OUTPUT_ARGS("clone", "unsigned int", "pe_resource_t *", "GList *", "GList *")
643 int
644 pe__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 
704 PCMK__OUTPUT_ARGS("clone", "unsigned int", "pe_resource_t *", "GList *", "GList *")
705 int
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 
870  if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
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 
898  if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
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 
951 void
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 
985 enum rsc_role_e
986 clone_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 
1011 bool
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 
1026 gboolean
1027 pe__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 }
pcmk__cpg_host_t host
Definition: cpg.c:49
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:218
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition: clone.c:84
pe_node_t * pe_find_node(GList *node_list, const char *uname)
Definition: status.c:434
xmlNode * orig_xml
Definition: pe_types.h:325
void clone_print(pe_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: clone.c:428
GHashTable * known_on
Definition: pe_types.h:368
void pcmk__add_separated_word(char **list, size_t *len, const char *word, const char *separator)
Definition: strings.c:702
bool is_set_recursive(pe_resource_t *rsc, long long flag, bool any)
Definition: clone.c:397
#define PROMOTED_INSTANCES
Definition: clone.c:27
Control output from tools.
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
gboolean clone_unpack(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: clone.c:154
GList * children
Definition: pe_types.h:378
#define UNPROMOTED_INSTANCES
Definition: clone.c:28
xmlNode * xml
Definition: pe_types.h:324
#define XML_RSC_ATTR_INCARNATION
Definition: msg_xml.h:225
gboolean pcmk__str_in_list(GList *lst, const gchar *s, uint32_t flags)
Definition: strings.c:895
#define pcmk__config_err(fmt...)
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
GHashTable * meta
Definition: pe_types.h:374
#define pe_rsc_unique
Definition: pe_types.h:254
resource_object_functions_t * fns
Definition: pe_types.h:333
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
pe_resource_t * pe_find_resource(GList *rsc_list, const char *id_rh)
Definition: status.c:382
#define PCMK_XE_PROMOTED_NODE_MAX_LEGACY
Definition: msg_xml.h:44
#define XML_RSC_ATTR_STICKINESS
Definition: msg_xml.h:236
enum crm_ais_msg_types type
Definition: cpg.c:48
enum rsc_role_e clone_resource_state(const pe_resource_t *rsc, gboolean current)
Definition: clone.c:986
#define XML_RSC_ATTR_INCARNATION_MAX
Definition: msg_xml.h:226
char * pending_task
Definition: pe_types.h:347
#define PCMK_XE_PROMOTED_MAX_LEGACY
Definition: msg_xml.h:43
gboolean pe__clone_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: clone.c:1027
int pe__clone_xml(pcmk__output_t *out, va_list args)
Definition: clone.c:644
GList * nodes
Definition: pe_types.h:157
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:829
void pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
Definition: utils.c:1944
const char * role2text(enum rsc_role_e role)
Definition: common.c:459
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition: pe_types.h:54
#define pe_warn(fmt...)
Definition: internal.h:27
int rc
Definition: pcmk_fence.c:35
#define pe_rsc_failed
Definition: pe_types.h:267
#define XML_ATTR_ID
Definition: msg_xml.h:129
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:530
#define XML_CIB_TAG_RESOURCE
Definition: msg_xml.h:214
#define PCMK__OUTPUT_ARGS(ARGS...)
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 clone_free(pe_resource_t *rsc)
Definition: clone.c:952
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:114
struct pe_node_shared_s * details
Definition: pe_types.h:244
void common_free(pe_resource_t *rsc)
Definition: complex.c:917
unsigned long long flags
Definition: pe_types.h:349
const char * uname
Definition: pe_types.h:209
#define pe_rsc_promotable
Definition: pe_types.h:256
#define XML_RSC_ATTR_ORDERED
Definition: msg_xml.h:223
void(* print)(pe_resource_t *, const char *, long, void *)
Definition: pe_types.h:51
#define XML_RSC_ATTR_INCARNATION_NODEMAX
Definition: msg_xml.h:228
pe_resource_t * pe__create_clone_child(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: clone.c:102
#define XML_RSC_ATTR_TARGET_ROLE
Definition: msg_xml.h:233
void(* free)(pe_resource_t *)
Definition: pe_types.h:55
void free_xml(xmlNode *child)
Definition: xml.c:823
enum rsc_role_e text2role(const char *role)
Definition: common.c:488
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:979
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:511
#define XML_RSC_ATTR_UNIQUE
Definition: msg_xml.h:234
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
enum rsc_role_e(* state)(const pe_resource_t *, gboolean)
Definition: pe_types.h:53
#define XML_RSC_ATTR_PROMOTED_MAX
Definition: msg_xml.h:230
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
Cluster status and scheduling.
gboolean(* is_filtered)(pe_resource_t *, GList *, gboolean)
Definition: pe_types.h:57
bool pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node)
Definition: utils.c:2412
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition: common.c:579
void * variant_opaque
Definition: pe_types.h:332
#define CRM_ASSERT(expr)
Definition: results.h:42
const char * rsc_printable_id(pe_resource_t *rsc)
Definition: utils.c:1917
#define status_print(fmt, args...)
This structure contains everything that makes up a single output formatter.
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
#define XML_RSC_ATTR_PROMOTED_NODEMAX
Definition: msg_xml.h:231
GList * running_on
Definition: pe_types.h:367
#define pe_rsc_failure_ignored
Definition: pe_types.h:275
gboolean common_unpack(xmlNode *xml_obj, pe_resource_t **rsc, pe_resource_t *parent, pe_working_set_t *data_set)
Definition: complex.c:493
gboolean clone_active(pe_resource_t *rsc, gboolean all)
Definition: clone.c:295
gboolean crm_is_true(const char *s)
Definition: strings.c:415
#define XML_CIB_TAG_GROUP
Definition: msg_xml.h:215
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:20
bool pe__resource_is_disabled(pe_resource_t *rsc)
Definition: utils.c:2359
#define ID(x)
Definition: msg_xml.h:456
#define pe_err(fmt...)
Definition: internal.h:22
gint sort_node_uname(gconstpointer a, gconstpointer b)
Definition: utils.c:218
gboolean unclean
Definition: pe_types.h:217
#define pe_rsc_managed
Definition: pe_types.h:249
#define pe_rsc_orphan
Definition: pe_types.h:248
gboolean online
Definition: pe_types.h:213
gboolean(* active)(pe_resource_t *, gboolean)
Definition: pe_types.h:52
int pe__clone_default(pcmk__output_t *out, va_list args)
Definition: clone.c:706
char * id
Definition: pe_types.h:322
GHashTable * allowed_nodes
Definition: pe_types.h:369