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