pacemaker  2.1.2-ada5c3b36
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(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
662  (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
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(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
734  (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
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(host->details->uname, only_node,
837  continue;
838  }
839 
840  pcmk__add_word(&list_text, &list_text_len, host->details->uname);
841  active_instances++;
842  }
843  g_list_free(promoted_list);
844 
845  if (list_text != NULL) {
846  clone_header(out, &rc, rsc, clone_data);
847 
848  out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]", list_text);
849  free(list_text);
850  list_text = NULL;
851  list_text_len = 0;
852  }
853 
854  /* Started/Unpromoted */
855  started_list = g_list_sort(started_list, sort_node_uname);
856  for (gIter = started_list; gIter; gIter = gIter->next) {
857  pe_node_t *host = gIter->data;
858 
859  if (!pcmk__str_in_list(host->details->uname, only_node,
861  continue;
862  }
863 
864  pcmk__add_word(&list_text, &list_text_len, host->details->uname);
865  active_instances++;
866  }
867  g_list_free(started_list);
868 
869  if (list_text != NULL) {
870  clone_header(out, &rc, rsc, clone_data);
871 
872  if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
873  enum rsc_role_e role = configured_role(rsc);
874 
875  if (role == RSC_ROLE_UNPROMOTED) {
876  out->list_item(out, NULL,
877  UNPROMOTED_INSTANCES " (target-role): [ %s ]",
878  list_text);
879  } else {
880  out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
881  list_text);
882  }
883 
884  } else {
885  out->list_item(out, NULL, "Started: [ %s ]", list_text);
886  }
887  free(list_text);
888  list_text = NULL;
889  list_text_len = 0;
890  }
891 
892  if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
893  const char *state = "Stopped";
894  enum rsc_role_e role = configured_role(rsc);
895 
896  if (role == RSC_ROLE_STOPPED) {
897  state = "Stopped (disabled)";
898  }
899 
900  if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
901  && (clone_data->clone_max > active_instances)) {
902 
903  GList *nIter;
904  GList *list = g_hash_table_get_values(rsc->allowed_nodes);
905 
906  /* Custom stopped list for non-unique clones */
907  free(stopped_list);
908  stopped_list = NULL;
909  stopped_list_len = 0;
910 
911  if (list == NULL) {
912  /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
913  * If we've not probed for them yet, the Stopped list will be empty
914  */
915  list = g_hash_table_get_values(rsc->known_on);
916  }
917 
918  list = g_list_sort(list, sort_node_uname);
919  for (nIter = list; nIter != NULL; nIter = nIter->next) {
920  pe_node_t *node = (pe_node_t *)nIter->data;
921 
922  if (pe_find_node(rsc->running_on, node->details->uname) == NULL &&
923  pcmk__str_in_list(node->details->uname, only_node,
925  pcmk__add_word(&stopped_list, &stopped_list_len,
926  node->details->uname);
927  }
928  }
929  g_list_free(list);
930  }
931 
932  if (stopped_list != NULL) {
933  clone_header(out, &rc, rsc, clone_data);
934 
935  out->list_item(out, NULL, "%s: [ %s ]", state, stopped_list);
936  free(stopped_list);
937  stopped_list_len = 0;
938 
939  /* If there are no instances of this clone (perhaps because there are no
940  * nodes configured), simply output the clone header by itself. This can
941  * come up in PCS testing.
942  */
943  } else if (active_instances == 0) {
944  clone_header(out, &rc, rsc, clone_data);
946  return rc;
947  }
948  }
949 
951  return rc;
952 }
953 
954 void
956 {
957  clone_variant_data_t *clone_data = NULL;
958 
959  get_clone_variant_data(clone_data, rsc);
960 
961  pe_rsc_trace(rsc, "Freeing %s", rsc->id);
962 
963  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
964  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
965 
966  CRM_ASSERT(child_rsc);
967  pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
968  free_xml(child_rsc->xml);
969  child_rsc->xml = NULL;
970  /* There could be a saved unexpanded xml */
971  free_xml(child_rsc->orig_xml);
972  child_rsc->orig_xml = NULL;
973  child_rsc->fns->free(child_rsc);
974  }
975 
976  g_list_free(rsc->children);
977 
978  if (clone_data) {
979  CRM_ASSERT(clone_data->demote_notify == NULL);
980  CRM_ASSERT(clone_data->stop_notify == NULL);
981  CRM_ASSERT(clone_data->start_notify == NULL);
982  CRM_ASSERT(clone_data->promote_notify == NULL);
983  }
984 
985  common_free(rsc);
986 }
987 
988 enum rsc_role_e
989 clone_resource_state(const pe_resource_t * rsc, gboolean current)
990 {
991  enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN;
992  GList *gIter = rsc->children;
993 
994  for (; gIter != NULL; gIter = gIter->next) {
995  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
996  enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
997 
998  if (a_role > clone_role) {
999  clone_role = a_role;
1000  }
1001  }
1002 
1003  pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(clone_role));
1004  return clone_role;
1005 }
1006 
1014 bool
1016  pe_working_set_t *data_set)
1017 {
1018  if (pe_rsc_is_clone(rsc)) {
1019  clone_variant_data_t *clone_data = NULL;
1020 
1021  get_clone_variant_data(clone_data, rsc);
1022  if (clone_data->clone_max == g_list_length(data_set->nodes)) {
1023  return TRUE;
1024  }
1025  }
1026  return FALSE;
1027 }
1028 
1029 gboolean
1030 pe__clone_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
1031 {
1032  gboolean passes = FALSE;
1033  clone_variant_data_t *clone_data = NULL;
1034 
1036  passes = TRUE;
1037  } else {
1038  get_clone_variant_data(clone_data, rsc);
1039  passes = pcmk__str_in_list(ID(clone_data->xml_obj_child), only_rsc, pcmk__str_star_matches);
1040 
1041  if (!passes) {
1042  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1043  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1044 
1045  if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
1046  passes = TRUE;
1047  break;
1048  }
1049  }
1050  }
1051  }
1052 
1053  return !passes;
1054 }
pcmk__cpg_host_t host
Definition: cpg.c:49
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:225
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:367
void pcmk__add_separated_word(char **list, size_t *len, const char *word, const char *separator)
Definition: strings.c:703
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:377
#define UNPROMOTED_INSTANCES
Definition: clone.c:28
xmlNode * xml
Definition: pe_types.h:324
#define XML_RSC_ATTR_INCARNATION
Definition: msg_xml.h:225
#define pcmk__config_err(fmt...)
bool pe__is_universal_clone(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: clone.c:1015
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:373
#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:323
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:989
#define XML_RSC_ATTR_INCARNATION_MAX
Definition: msg_xml.h:226
char * pending_task
Definition: pe_types.h:346
#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:1030
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:830
void pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
Definition: utils.c:2038
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:529
#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:496
void clone_free(pe_resource_t *rsc)
Definition: clone.c:955
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:348
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
gboolean pcmk__str_in_list(const gchar *s, GList *lst, uint32_t flags)
Definition: strings.c:886
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:824
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:955
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:2482
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:2011
#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:366
#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:416
#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:2428
#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:368