pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
clone.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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/common/xml.h>
19 #include <crm/common/output.h>
22 
23 #ifdef PCMK__COMPAT_2_0
24 #define PROMOTED_INSTANCES PCMK__ROLE_PROMOTED_LEGACY "s"
25 #define UNPROMOTED_INSTANCES PCMK__ROLE_UNPROMOTED_LEGACY "s"
26 #else
27 #define PROMOTED_INSTANCES PCMK_ROLE_PROMOTED
28 #define UNPROMOTED_INSTANCES PCMK_ROLE_UNPROMOTED
29 #endif
30 
31 typedef struct clone_variant_data_s {
32  int clone_max;
33  int clone_node_max;
34 
35  int promoted_max;
36  int promoted_node_max;
37 
38  int total_clones;
39 
40  uint32_t flags; // Group of enum pcmk__clone_flags
41 
42  notify_data_t *stop_notify;
43  notify_data_t *start_notify;
44  notify_data_t *demote_notify;
45  notify_data_t *promote_notify;
46 
47  xmlNode *xml_obj_child;
49 
50 #define get_clone_variant_data(data, rsc) \
51  pcmk__assert(pcmk__is_clone(rsc) && (rsc->variant_opaque != NULL)); \
52  data = (clone_variant_data_t *) rsc->variant_opaque;
53 
62 int
64 {
65  const clone_variant_data_t *clone_data = NULL;
66 
67  get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
68  return clone_data->clone_max;
69 }
70 
79 int
81 {
82  const clone_variant_data_t *clone_data = NULL;
83 
84  get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
85  return clone_data->clone_node_max;
86 }
87 
96 int
98 {
99  clone_variant_data_t *clone_data = NULL;
100 
101  get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
102  return clone_data->promoted_max;
103 }
104 
113 int
115 {
116  clone_variant_data_t *clone_data = NULL;
117 
118  get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
119  return clone_data->promoted_node_max;
120 }
121 
122 static GList *
123 sorted_hash_table_values(GHashTable *table)
124 {
125  GList *retval = NULL;
126  GHashTableIter iter;
127  gpointer key, value;
128 
129  g_hash_table_iter_init(&iter, table);
130  while (g_hash_table_iter_next(&iter, &key, &value)) {
131  if (!g_list_find_custom(retval, value, (GCompareFunc) strcmp)) {
132  retval = g_list_prepend(retval, (char *) value);
133  }
134  }
135 
136  retval = g_list_sort(retval, (GCompareFunc) strcmp);
137  return retval;
138 }
139 
140 static GList *
141 nodes_with_status(GHashTable *table, const char *status)
142 {
143  GList *retval = NULL;
144  GHashTableIter iter;
145  gpointer key, value;
146 
147  g_hash_table_iter_init(&iter, table);
148  while (g_hash_table_iter_next(&iter, &key, &value)) {
149  if (!strcmp((char *) value, status)) {
150  retval = g_list_prepend(retval, key);
151  }
152  }
153 
154  retval = g_list_sort(retval, (GCompareFunc) pcmk__numeric_strcasecmp);
155  return retval;
156 }
157 
158 static GString *
159 node_list_to_str(const GList *list)
160 {
161  GString *retval = NULL;
162 
163  for (const GList *iter = list; iter != NULL; iter = iter->next) {
164  pcmk__add_word(&retval, 1024, (const char *) iter->data);
165  }
166 
167  return retval;
168 }
169 
170 static void
171 clone_header(pcmk__output_t *out, int *rc, const pcmk_resource_t *rsc,
172  clone_variant_data_t *clone_data, const char *desc)
173 {
174  GString *attrs = NULL;
175 
177  pcmk__add_separated_word(&attrs, 64, "promotable", ", ");
178  }
179 
180  if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
181  pcmk__add_separated_word(&attrs, 64, "unique", ", ");
182  }
183 
184  if (pe__resource_is_disabled(rsc)) {
185  pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
186  }
187 
189  pcmk__add_separated_word(&attrs, 64, "maintenance", ", ");
190 
191  } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
192  pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
193  }
194 
195  if (attrs != NULL) {
196  PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)%s%s%s",
197  rsc->id,
198  pcmk__xe_id(clone_data->xml_obj_child),
199  (const char *) attrs->str, desc ? " (" : "",
200  desc ? desc : "", desc ? ")" : "");
201  g_string_free(attrs, TRUE);
202  } else {
203  PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]%s%s%s",
204  rsc->id,
205  pcmk__xe_id(clone_data->xml_obj_child),
206  desc ? " (" : "", desc ? desc : "",
207  desc ? ")" : "");
208  }
209 }
210 
211 void
212 pe__force_anon(const char *standard, pcmk_resource_t *rsc, const char *rid,
214 {
215  if (pcmk__is_clone(rsc)) {
216  clone_variant_data_t *clone_data = rsc->variant_opaque;
217 
218  pcmk__config_warn("Ignoring " PCMK_META_GLOBALLY_UNIQUE " for %s "
219  "because %s resources such as %s can be used only as "
220  "anonymous clones", rsc->id, standard, rid);
221 
222  clone_data->clone_node_max = 1;
223  clone_data->clone_max = QB_MIN(clone_data->clone_max,
224  g_list_length(scheduler->nodes));
225  }
226 }
227 
229 find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
230 {
231  char *child_id = NULL;
232  pcmk_resource_t *child = NULL;
233  const char *child_base = NULL;
234  clone_variant_data_t *clone_data = NULL;
235 
236  get_clone_variant_data(clone_data, rsc);
237 
238  child_base = pcmk__xe_id(clone_data->xml_obj_child);
239  child_id = crm_strdup_printf("%s:%s", child_base, sub_id);
240  child = pe_find_resource(rsc->children, child_id);
241 
242  free(child_id);
243  return child;
244 }
245 
248 {
249  gboolean as_orphan = FALSE;
250  char *inc_num = NULL;
251  char *inc_max = NULL;
252  pcmk_resource_t *child_rsc = NULL;
253  xmlNode *child_copy = NULL;
254  clone_variant_data_t *clone_data = NULL;
255 
256  get_clone_variant_data(clone_data, rsc);
257 
258  CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
259 
260  if (clone_data->total_clones >= clone_data->clone_max) {
261  // If we've already used all available instances, this is an orphan
262  as_orphan = TRUE;
263  }
264 
265  // Allocate instance numbers in numerical order (starting at 0)
266  inc_num = pcmk__itoa(clone_data->total_clones);
267  inc_max = pcmk__itoa(clone_data->clone_max);
268 
269  child_copy = pcmk__xml_copy(NULL, clone_data->xml_obj_child);
270 
271  crm_xml_add(child_copy, PCMK__META_CLONE, inc_num);
272 
273  if (pe__unpack_resource(child_copy, &child_rsc, rsc,
274  scheduler) != pcmk_rc_ok) {
275  goto bail;
276  }
277 /* child_rsc->globally_unique = rsc->globally_unique; */
278 
279  pcmk__assert(child_rsc != NULL);
280  clone_data->total_clones += 1;
281  pcmk__rsc_trace(child_rsc, "Setting clone attributes for: %s",
282  child_rsc->id);
283  rsc->children = g_list_append(rsc->children, child_rsc);
284  if (as_orphan) {
286  }
287 
288  pcmk__insert_meta(child_rsc, PCMK_META_CLONE_MAX, inc_max);
289  pcmk__rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
290 
291  bail:
292  free(inc_num);
293  free(inc_max);
294 
295  return child_rsc;
296 }
297 
311 static int
312 unpack_meta_int(const pcmk_resource_t *rsc, const char *meta_name,
313  const char *deprecated_name, int default_value)
314 {
315  int integer = default_value;
316  const char *value = g_hash_table_lookup(rsc->meta, meta_name);
317 
318  if ((value == NULL) && (deprecated_name != NULL)) {
319  value = g_hash_table_lookup(rsc->meta, deprecated_name);
320 
321  if (value != NULL) {
322  if (pcmk__str_eq(deprecated_name, PCMK__META_PROMOTED_MAX_LEGACY,
323  pcmk__str_none)) {
325  "Support for the " PCMK__META_PROMOTED_MAX_LEGACY
326  " meta-attribute (such as in %s) is deprecated "
327  "and will be removed in a future release. Use the "
328  PCMK_META_PROMOTED_MAX " meta-attribute instead.",
329  rsc->id);
330  } else if (pcmk__str_eq(deprecated_name, PCMK__META_PROMOTED_NODE_MAX_LEGACY,
331  pcmk__str_none)) {
333  "Support for the " PCMK__META_PROMOTED_NODE_MAX_LEGACY
334  " meta-attribute (such as in %s) is deprecated "
335  "and will be removed in a future release. Use the "
336  PCMK_META_PROMOTED_NODE_MAX " meta-attribute instead.",
337  rsc->id);
338  }
339  }
340  }
341  if (value != NULL) {
342  pcmk__scan_min_int(value, &integer, 0);
343  }
344  return integer;
345 }
346 
347 gboolean
349 {
350  int lpc = 0;
351  xmlNode *a_child = NULL;
352  xmlNode *xml_obj = rsc->xml;
353  clone_variant_data_t *clone_data = NULL;
354 
355  pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
356 
357  clone_data = pcmk__assert_alloc(1, sizeof(clone_variant_data_t));
358  rsc->variant_opaque = clone_data;
359 
361  // Use 1 as default but 0 for minimum and invalid
362  // @COMPAT PCMK__META_PROMOTED_MAX_LEGACY deprecated since 2.0.0
363  clone_data->promoted_max =
364  unpack_meta_int(rsc, PCMK_META_PROMOTED_MAX,
366 
367  // Use 1 as default but 0 for minimum and invalid
368  // @COMPAT PCMK__META_PROMOTED_NODE_MAX_LEGACY deprecated since 2.0.0
369  clone_data->promoted_node_max =
370  unpack_meta_int(rsc, PCMK_META_PROMOTED_NODE_MAX,
372  }
373 
374  // Use 1 as default but 0 for minimum and invalid
375  clone_data->clone_node_max = unpack_meta_int(rsc, PCMK_META_CLONE_NODE_MAX,
376  NULL, 1);
377 
378  /* Use number of nodes (but always at least 1, which is handy for crm_verify
379  * for a CIB without nodes) as default, but 0 for minimum and invalid
380  */
381  clone_data->clone_max = unpack_meta_int(rsc, PCMK_META_CLONE_MAX, NULL,
382  QB_MAX(1, g_list_length(scheduler->nodes)));
383 
384  if (crm_is_true(g_hash_table_lookup(rsc->meta, PCMK_META_ORDERED))) {
385  clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
386  "Clone", rsc->id,
387  clone_data->flags,
389  "pcmk__clone_ordered");
390  }
391 
392  if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
393  && (clone_data->clone_node_max > 1)) {
394 
395  pcmk__config_err("Ignoring " PCMK_META_CLONE_NODE_MAX " of %d for %s "
396  "because anonymous clones support only one instance "
397  "per node", clone_data->clone_node_max, rsc->id);
398  clone_data->clone_node_max = 1;
399  }
400 
401  pcmk__rsc_trace(rsc, "Options for %s", rsc->id);
402  pcmk__rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
403  pcmk__rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
404  pcmk__rsc_trace(rsc, "\tClone is unique: %s",
405  pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
406  pcmk__rsc_trace(rsc, "\tClone is promotable: %s",
407  pcmk__flag_text(rsc->flags, pcmk_rsc_promotable));
408 
409  // Clones may contain a single group or primitive
410  for (a_child = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
411  a_child != NULL; a_child = pcmk__xe_next(a_child)) {
412 
413  if (pcmk__str_any_of((const char *) a_child->name,
415  clone_data->xml_obj_child = a_child;
416  break;
417  }
418  }
419 
420  if (clone_data->xml_obj_child == NULL) {
421  pcmk__config_err("%s has nothing to clone", rsc->id);
422  return FALSE;
423  }
424 
425  /*
426  * Make clones ever so slightly sticky by default
427  *
428  * This helps ensure clone instances are not shuffled around the cluster
429  * for no benefit in situations when pre-allocation is not appropriate
430  */
431  if (g_hash_table_lookup(rsc->meta, PCMK_META_RESOURCE_STICKINESS) == NULL) {
433  }
434 
435  /* This ensures that the PCMK_META_GLOBALLY_UNIQUE value always exists for
436  * children to inherit when being unpacked, as well as in resource agents'
437  * environment.
438  */
440  pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
441 
442  if (clone_data->clone_max <= 0) {
443  /* Create one child instance so that unpack_find_resource() will hook up
444  * any orphans up to the parent correctly.
445  */
446  if (pe__create_clone_child(rsc, scheduler) == NULL) {
447  return FALSE;
448  }
449 
450  } else {
451  // Create a child instance for each available instance number
452  for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
453  if (pe__create_clone_child(rsc, scheduler) == NULL) {
454  return FALSE;
455  }
456  }
457  }
458 
459  pcmk__rsc_trace(rsc, "Added %d children to resource %s...",
460  clone_data->clone_max, rsc->id);
461  return TRUE;
462 }
463 
464 gboolean
465 clone_active(pcmk_resource_t * rsc, gboolean all)
466 {
467  GList *gIter = rsc->children;
468 
469  for (; gIter != NULL; gIter = gIter->next) {
470  pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
471  gboolean child_active = child_rsc->fns->active(child_rsc, all);
472 
473  if (all == FALSE && child_active) {
474  return TRUE;
475  } else if (all && child_active == FALSE) {
476  return FALSE;
477  }
478  }
479 
480  if (all) {
481  return TRUE;
482  } else {
483  return FALSE;
484  }
485 }
486 
491 static void
492 short_print(const char *list, const char *prefix, const char *type,
493  const char *suffix, long options, void *print_data)
494 {
495  if(suffix == NULL) {
496  suffix = "";
497  }
498 
499  if (!pcmk__str_empty(list)) {
500  if (options & pe_print_html) {
501  status_print("<li>");
502  }
503  status_print("%s%s: [ %s ]%s", prefix, type, list, suffix);
504 
505  if (options & pe_print_html) {
506  status_print("</li>\n");
507 
508  } else if (options & pe_print_suppres_nl) {
509  /* nothing */
510  } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
511  status_print("\n");
512  }
513 
514  }
515 }
516 
517 static const char *
518 configured_role_str(pcmk_resource_t * rsc)
519 {
520  const char *target_role = g_hash_table_lookup(rsc->meta,
522 
523  if ((target_role == NULL) && rsc->children && rsc->children->data) {
524  pcmk_resource_t *instance = rsc->children->data; // Any instance will do
525 
526  target_role = g_hash_table_lookup(instance->meta,
528  }
529  return target_role;
530 }
531 
532 static enum rsc_role_e
533 configured_role(pcmk_resource_t *rsc)
534 {
535  enum rsc_role_e role = pcmk_role_unknown;
536  const char *target_role = configured_role_str(rsc);
537 
538  if (target_role != NULL) {
539  role = pcmk_parse_role(target_role);
540  if (role == pcmk_role_unknown) {
542  " for resource %s", rsc->id);
543  }
544  }
545  return role;
546 }
547 
552 static void
553 clone_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
554  void *print_data)
555 {
556  char *child_text = crm_strdup_printf("%s ", pre_text);
557  const char *target_role = configured_role_str(rsc);
558  GList *gIter = rsc->children;
559 
560  status_print("%s<clone ", pre_text);
561  status_print(PCMK_XA_ID "=\"%s\" ", rsc->id);
562  status_print("multi_state=\"%s\" ",
563  pcmk__flag_text(rsc->flags, pcmk_rsc_promotable));
564  status_print("unique=\"%s\" ",
565  pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
566  status_print("managed=\"%s\" ",
567  pcmk__flag_text(rsc->flags, pcmk_rsc_managed));
568  status_print("failed=\"%s\" ",
569  pcmk__flag_text(rsc->flags, pcmk_rsc_failed));
570  status_print("failure_ignored=\"%s\" ",
571  pcmk__flag_text(rsc->flags, pcmk_rsc_ignore_failure));
572  if (target_role) {
573  status_print("target_role=\"%s\" ", target_role);
574  }
575  status_print(">\n");
576 
577  for (; gIter != NULL; gIter = gIter->next) {
578  pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
579 
580  child_rsc->fns->print(child_rsc, child_text, options, print_data);
581  }
582 
583  status_print("%s</clone>\n", pre_text);
584  free(child_text);
585 }
586 
587 bool
588 is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any)
589 {
590  GList *gIter;
591  bool all = !any;
592 
593  if (pcmk_is_set(rsc->flags, flag)) {
594  if(any) {
595  return TRUE;
596  }
597  } else if(all) {
598  return FALSE;
599  }
600 
601  for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
602  if(is_set_recursive(gIter->data, flag, any)) {
603  if(any) {
604  return TRUE;
605  }
606 
607  } else if(all) {
608  return FALSE;
609  }
610  }
611 
612  if(all) {
613  return TRUE;
614  }
615  return FALSE;
616 }
617 
622 void
623 clone_print(pcmk_resource_t *rsc, const char *pre_text, long options,
624  void *print_data)
625 {
626  GString *list_text = NULL;
627  char *child_text = NULL;
628  GString *stopped_list = NULL;
629 
630  GList *promoted_list = NULL;
631  GList *started_list = NULL;
632  GList *gIter = rsc->children;
633 
634  clone_variant_data_t *clone_data = NULL;
635  int active_instances = 0;
636 
637  if (pre_text == NULL) {
638  pre_text = " ";
639  }
640 
641  if (options & pe_print_xml) {
642  clone_print_xml(rsc, pre_text, options, print_data);
643  return;
644  }
645 
646  get_clone_variant_data(clone_data, rsc);
647 
648  child_text = crm_strdup_printf("%s ", pre_text);
649 
650  status_print("%sClone Set: %s [%s]%s%s%s",
651  pcmk__s(pre_text, ""), rsc->id,
652  pcmk__xe_id(clone_data->xml_obj_child),
653  pcmk_is_set(rsc->flags, pcmk_rsc_promotable)? " (promotable)" : "",
654  pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
655  pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " (unmanaged)");
656 
657  if (options & pe_print_html) {
658  status_print("\n<ul>\n");
659 
660  } else if ((options & pe_print_log) == 0) {
661  status_print("\n");
662  }
663 
664  for (; gIter != NULL; gIter = gIter->next) {
665  gboolean print_full = FALSE;
666  pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
667  gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
668 
669  if (options & pe_print_clone_details) {
670  print_full = TRUE;
671  }
672 
673  if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
674  // Print individual instance when unique (except stopped orphans)
675  if (partially_active
676  || !pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
677  print_full = TRUE;
678  }
679 
680  // Everything else in this block is for anonymous clones
681 
682  } else if (pcmk_is_set(options, pe_print_pending)
683  && (child_rsc->pending_task != NULL)
684  && strcmp(child_rsc->pending_task, "probe")) {
685  // Print individual instance when non-probe action is pending
686  print_full = TRUE;
687 
688  } else if (partially_active == FALSE) {
689  // List stopped instances when requested (except orphans)
690  if (!pcmk_is_set(child_rsc->flags, pcmk_rsc_removed)
691  && !pcmk_is_set(options, pe_print_clone_active)) {
692 
693  pcmk__add_word(&stopped_list, 1024, child_rsc->id);
694  }
695 
696  } else if (is_set_recursive(child_rsc, pcmk_rsc_removed, TRUE)
697  || !is_set_recursive(child_rsc, pcmk_rsc_managed, FALSE)
698  || is_set_recursive(child_rsc, pcmk_rsc_failed, TRUE)) {
699 
700  // Print individual instance when active orphaned/unmanaged/failed
701  print_full = TRUE;
702 
703  } else if (child_rsc->fns->active(child_rsc, TRUE)) {
704  // Instance of fully active anonymous clone
705 
706  pcmk_node_t *location = NULL;
707 
708  location = child_rsc->fns->location(child_rsc, NULL, TRUE);
709  if (location) {
710  // Instance is active on a single node
711 
712  enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
713 
714  if (location->details->online == FALSE && location->details->unclean) {
715  print_full = TRUE;
716 
717  } else if (a_role > pcmk_role_unpromoted) {
718  promoted_list = g_list_append(promoted_list, location);
719 
720  } else {
721  started_list = g_list_append(started_list, location);
722  }
723 
724  } else {
725  /* uncolocated group - bleh */
726  print_full = TRUE;
727  }
728 
729  } else {
730  // Instance of partially active anonymous clone
731  print_full = TRUE;
732  }
733 
734  if (print_full) {
735  if (options & pe_print_html) {
736  status_print("<li>\n");
737  }
738  child_rsc->fns->print(child_rsc, child_text, options, print_data);
739  if (options & pe_print_html) {
740  status_print("</li>\n");
741  }
742  }
743  }
744 
745  /* Promoted */
746  promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
747  for (gIter = promoted_list; gIter; gIter = gIter->next) {
748  pcmk_node_t *host = gIter->data;
749 
750  pcmk__add_word(&list_text, 1024, host->details->uname);
751  active_instances++;
752  }
753 
754  if (list_text != NULL) {
755  short_print((const char *) list_text->str, child_text,
756  PROMOTED_INSTANCES, NULL, options, print_data);
757  g_string_truncate(list_text, 0);
758  }
759  g_list_free(promoted_list);
760 
761  /* Started/Unpromoted */
762  started_list = g_list_sort(started_list, pe__cmp_node_name);
763  for (gIter = started_list; gIter; gIter = gIter->next) {
764  pcmk_node_t *host = gIter->data;
765 
766  pcmk__add_word(&list_text, 1024, host->details->uname);
767  active_instances++;
768  }
769 
770  if (list_text != NULL) {
772  enum rsc_role_e role = configured_role(rsc);
773 
774  if (role == pcmk_role_unpromoted) {
775  short_print((const char *) list_text->str, child_text,
777  NULL, options, print_data);
778  } else {
779  short_print((const char *) list_text->str, child_text,
780  UNPROMOTED_INSTANCES, NULL, options, print_data);
781  }
782 
783  } else {
784  short_print((const char *) list_text->str, child_text, "Started",
785  NULL, options, print_data);
786  }
787  }
788 
789  g_list_free(started_list);
790 
791  if (!pcmk_is_set(options, pe_print_clone_active)) {
792  const char *state = "Stopped";
793  enum rsc_role_e role = configured_role(rsc);
794 
795  if (role == pcmk_role_stopped) {
796  state = "Stopped (disabled)";
797  }
798 
799  if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
800  && (clone_data->clone_max > active_instances)) {
801 
802  GList *nIter;
803  GList *list = g_hash_table_get_values(rsc->allowed_nodes);
804 
805  /* Custom stopped list for non-unique clones */
806  if (stopped_list != NULL) {
807  g_string_truncate(stopped_list, 0);
808  }
809 
810  if (list == NULL) {
811  /* Clusters with PCMK_OPT_SYMMETRIC_CLUSTER=false haven't
812  * calculated allowed_nodes yet. If we've not probed for them
813  * yet, the Stopped list will be empty.
814  */
815  list = g_hash_table_get_values(rsc->known_on);
816  }
817 
818  list = g_list_sort(list, pe__cmp_node_name);
819  for (nIter = list; nIter != NULL; nIter = nIter->next) {
820  pcmk_node_t *node = (pcmk_node_t *) nIter->data;
821 
823  node->details->uname) == NULL) {
824  pcmk__add_word(&stopped_list, 1024, node->details->uname);
825  }
826  }
827  g_list_free(list);
828  }
829 
830  if (stopped_list != NULL) {
831  short_print((const char *) stopped_list->str, child_text, state,
832  NULL, options, print_data);
833  }
834  }
835 
836  if (options & pe_print_html) {
837  status_print("</ul>\n");
838  }
839 
840  if (list_text != NULL) {
841  g_string_free(list_text, TRUE);
842  }
843 
844  if (stopped_list != NULL) {
845  g_string_free(stopped_list, TRUE);
846  }
847  free(child_text);
848 }
849 
850 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
851  "GList *")
852 int
853 pe__clone_xml(pcmk__output_t *out, va_list args)
854 {
855  uint32_t show_opts = va_arg(args, uint32_t);
856  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
857  GList *only_node = va_arg(args, GList *);
858  GList *only_rsc = va_arg(args, GList *);
859 
860  GList *gIter = rsc->children;
861  GList *all = NULL;
862  int rc = pcmk_rc_no_output;
863  gboolean printed_header = FALSE;
864  gboolean print_everything = TRUE;
865 
866  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
867  return rc;
868  }
869 
870  print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
871  (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
872 
873  all = g_list_prepend(all, (gpointer) "*");
874 
875  for (; gIter != NULL; gIter = gIter->next) {
876  pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
877 
878  if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
879  continue;
880  }
881 
882  if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
883  continue;
884  }
885 
886  if (!printed_header) {
887  const char *multi_state = pcmk__flag_text(rsc->flags,
889  const char *unique = pcmk__flag_text(rsc->flags, pcmk_rsc_unique);
890  const char *maintenance = pcmk__flag_text(rsc->flags,
892  const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed);
893  const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc));
894  const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed);
895  const char *ignored = pcmk__flag_text(rsc->flags,
897  const char *target_role = configured_role_str(rsc);
898  const char *desc = pe__resource_description(rsc, show_opts);
899 
900  printed_header = TRUE;
901 
902  rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_CLONE,
903  PCMK_XA_ID, rsc->id,
904  PCMK_XA_MULTI_STATE, multi_state,
905  PCMK_XA_UNIQUE, unique,
906  PCMK_XA_MAINTENANCE, maintenance,
907  PCMK_XA_MANAGED, managed,
908  PCMK_XA_DISABLED, disabled,
909  PCMK_XA_FAILED, failed,
910  PCMK_XA_FAILURE_IGNORED, ignored,
911  PCMK_XA_TARGET_ROLE, target_role,
912  PCMK_XA_DESCRIPTION, desc,
913  NULL);
914  pcmk__assert(rc == pcmk_rc_ok);
915  }
916 
917  out->message(out, (const char *) child_rsc->xml->name, show_opts,
918  child_rsc, only_node, all);
919  }
920 
921  if (printed_header) {
923  }
924 
925  g_list_free(all);
926  return rc;
927 }
928 
929 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
930  "GList *")
931 int
932 pe__clone_default(pcmk__output_t *out, va_list args)
933 {
934  uint32_t show_opts = va_arg(args, uint32_t);
935  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
936  GList *only_node = va_arg(args, GList *);
937  GList *only_rsc = va_arg(args, GList *);
938 
939  GHashTable *stopped = NULL;
940 
941  GString *list_text = NULL;
942 
943  GList *promoted_list = NULL;
944  GList *started_list = NULL;
945  GList *gIter = rsc->children;
946 
947  const char *desc = NULL;
948 
949  clone_variant_data_t *clone_data = NULL;
950  int active_instances = 0;
951  int rc = pcmk_rc_no_output;
952  gboolean print_everything = TRUE;
953 
954  desc = pe__resource_description(rsc, show_opts);
955 
956  get_clone_variant_data(clone_data, rsc);
957 
958  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
959  return rc;
960  }
961 
962  print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
963  (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
964 
965  for (; gIter != NULL; gIter = gIter->next) {
966  gboolean print_full = FALSE;
967  pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
968  gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
969 
970  if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
971  continue;
972  }
973 
974  if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
975  continue;
976  }
977 
978  if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
979  print_full = TRUE;
980  }
981 
982  if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
983  // Print individual instance when unique (except stopped orphans)
984  if (partially_active
985  || !pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
986  print_full = TRUE;
987  }
988 
989  // Everything else in this block is for anonymous clones
990 
991  } else if (pcmk_is_set(show_opts, pcmk_show_pending)
992  && (child_rsc->pending_task != NULL)
993  && strcmp(child_rsc->pending_task, "probe")) {
994  // Print individual instance when non-probe action is pending
995  print_full = TRUE;
996 
997  } else if (partially_active == FALSE) {
998  // List stopped instances when requested (except orphans)
999  if (!pcmk_is_set(child_rsc->flags, pcmk_rsc_removed)
1000  && !pcmk_is_set(show_opts, pcmk_show_clone_detail)
1001  && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
1002  if (stopped == NULL) {
1003  stopped = pcmk__strkey_table(free, free);
1004  }
1005  pcmk__insert_dup(stopped, child_rsc->id, "Stopped");
1006  }
1007 
1008  } else if (is_set_recursive(child_rsc, pcmk_rsc_removed, TRUE)
1009  || !is_set_recursive(child_rsc, pcmk_rsc_managed, FALSE)
1010  || is_set_recursive(child_rsc, pcmk_rsc_failed, TRUE)) {
1011 
1012  // Print individual instance when active orphaned/unmanaged/failed
1013  print_full = TRUE;
1014 
1015  } else if (child_rsc->fns->active(child_rsc, TRUE)) {
1016  // Instance of fully active anonymous clone
1017 
1018  pcmk_node_t *location = NULL;
1019 
1020  location = child_rsc->fns->location(child_rsc, NULL, TRUE);
1021  if (location) {
1022  // Instance is active on a single node
1023 
1024  enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
1025 
1026  if (location->details->online == FALSE && location->details->unclean) {
1027  print_full = TRUE;
1028 
1029  } else if (a_role > pcmk_role_unpromoted) {
1030  promoted_list = g_list_append(promoted_list, location);
1031 
1032  } else {
1033  started_list = g_list_append(started_list, location);
1034  }
1035 
1036  } else {
1037  /* uncolocated group - bleh */
1038  print_full = TRUE;
1039  }
1040 
1041  } else {
1042  // Instance of partially active anonymous clone
1043  print_full = TRUE;
1044  }
1045 
1046  if (print_full) {
1047  GList *all = NULL;
1048 
1049  clone_header(out, &rc, rsc, clone_data, desc);
1050 
1051  /* Print every resource that's a child of this clone. */
1052  all = g_list_prepend(all, (gpointer) "*");
1053  out->message(out, (const char *) child_rsc->xml->name, show_opts,
1054  child_rsc, only_node, all);
1055  g_list_free(all);
1056  }
1057  }
1058 
1059  if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
1060  PCMK__OUTPUT_LIST_FOOTER(out, rc);
1061  return pcmk_rc_ok;
1062  }
1063 
1064  /* Promoted */
1065  promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
1066  for (gIter = promoted_list; gIter; gIter = gIter->next) {
1067  pcmk_node_t *host = gIter->data;
1068 
1069  if (!pcmk__str_in_list(host->details->uname, only_node,
1071  continue;
1072  }
1073 
1074  pcmk__add_word(&list_text, 1024, host->details->uname);
1075  active_instances++;
1076  }
1077  g_list_free(promoted_list);
1078 
1079  if ((list_text != NULL) && (list_text->len > 0)) {
1080  clone_header(out, &rc, rsc, clone_data, desc);
1081 
1082  out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]",
1083  (const char *) list_text->str);
1084  g_string_truncate(list_text, 0);
1085  }
1086 
1087  /* Started/Unpromoted */
1088  started_list = g_list_sort(started_list, pe__cmp_node_name);
1089  for (gIter = started_list; gIter; gIter = gIter->next) {
1090  pcmk_node_t *host = gIter->data;
1091 
1092  if (!pcmk__str_in_list(host->details->uname, only_node,
1094  continue;
1095  }
1096 
1097  pcmk__add_word(&list_text, 1024, host->details->uname);
1098  active_instances++;
1099  }
1100  g_list_free(started_list);
1101 
1102  if ((list_text != NULL) && (list_text->len > 0)) {
1103  clone_header(out, &rc, rsc, clone_data, desc);
1104 
1105  if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
1106  enum rsc_role_e role = configured_role(rsc);
1107 
1108  if (role == pcmk_role_unpromoted) {
1109  out->list_item(out, NULL,
1111  " (" PCMK_META_TARGET_ROLE "): [ %s ]",
1112  (const char *) list_text->str);
1113  } else {
1114  out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
1115  (const char *) list_text->str);
1116  }
1117 
1118  } else {
1119  out->list_item(out, NULL, "Started: [ %s ]",
1120  (const char *) list_text->str);
1121  }
1122  }
1123 
1124  if (list_text != NULL) {
1125  g_string_free(list_text, TRUE);
1126  }
1127 
1128  if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
1129  if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)
1130  && (clone_data->clone_max > active_instances)) {
1131 
1132  GList *nIter;
1133  GList *list = g_hash_table_get_values(rsc->allowed_nodes);
1134 
1135  /* Custom stopped table for non-unique clones */
1136  if (stopped != NULL) {
1137  g_hash_table_destroy(stopped);
1138  stopped = NULL;
1139  }
1140 
1141  if (list == NULL) {
1142  /* Clusters with PCMK_OPT_SYMMETRIC_CLUSTER=false haven't
1143  * calculated allowed_nodes yet. If we've not probed for them
1144  * yet, the Stopped list will be empty.
1145  */
1146  list = g_hash_table_get_values(rsc->known_on);
1147  }
1148 
1149  list = g_list_sort(list, pe__cmp_node_name);
1150  for (nIter = list; nIter != NULL; nIter = nIter->next) {
1151  pcmk_node_t *node = (pcmk_node_t *) nIter->data;
1152 
1154  node->details->uname) == NULL)
1155  && pcmk__str_in_list(node->details->uname, only_node,
1157  xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node->details->uname);
1158  const char *state = "Stopped";
1159 
1160  if (configured_role(rsc) == pcmk_role_stopped) {
1161  state = "Stopped (disabled)";
1162  }
1163 
1164  if (stopped == NULL) {
1165  stopped = pcmk__strkey_table(free, free);
1166  }
1167  if (probe_op != NULL) {
1168  int rc;
1169 
1172  &rc, 0);
1173  g_hash_table_insert(stopped, strdup(node->details->uname),
1174  crm_strdup_printf("Stopped (%s)", services_ocf_exitcode_str(rc)));
1175  } else {
1176  pcmk__insert_dup(stopped, node->details->uname, state);
1177  }
1178  }
1179  }
1180  g_list_free(list);
1181  }
1182 
1183  if (stopped != NULL) {
1184  GList *list = sorted_hash_table_values(stopped);
1185 
1186  clone_header(out, &rc, rsc, clone_data, desc);
1187 
1188  for (GList *status_iter = list; status_iter != NULL; status_iter = status_iter->next) {
1189  const char *status = status_iter->data;
1190  GList *nodes = nodes_with_status(stopped, status);
1191  GString *nodes_str = node_list_to_str(nodes);
1192 
1193  if (nodes_str != NULL) {
1194  if (nodes_str->len > 0) {
1195  out->list_item(out, NULL, "%s: [ %s ]", status,
1196  (const char *) nodes_str->str);
1197  }
1198  g_string_free(nodes_str, TRUE);
1199  }
1200 
1201  g_list_free(nodes);
1202  }
1203 
1204  g_list_free(list);
1205  g_hash_table_destroy(stopped);
1206 
1207  /* If there are no instances of this clone (perhaps because there are no
1208  * nodes configured), simply output the clone header by itself. This can
1209  * come up in PCS testing.
1210  */
1211  } else if (active_instances == 0) {
1212  clone_header(out, &rc, rsc, clone_data, desc);
1213  PCMK__OUTPUT_LIST_FOOTER(out, rc);
1214  return rc;
1215  }
1216  }
1217 
1218  PCMK__OUTPUT_LIST_FOOTER(out, rc);
1219  return rc;
1220 }
1221 
1222 void
1224 {
1225  clone_variant_data_t *clone_data = NULL;
1226 
1227  get_clone_variant_data(clone_data, rsc);
1228 
1229  pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
1230 
1231  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1232  pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
1233 
1234  pcmk__assert(child_rsc);
1235  pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
1236  free_xml(child_rsc->xml);
1237  child_rsc->xml = NULL;
1238  /* There could be a saved unexpanded xml */
1239  free_xml(child_rsc->orig_xml);
1240  child_rsc->orig_xml = NULL;
1241  child_rsc->fns->free(child_rsc);
1242  }
1243 
1244  g_list_free(rsc->children);
1245 
1246  if (clone_data) {
1247  pcmk__assert((clone_data->demote_notify == NULL)
1248  && (clone_data->stop_notify == NULL)
1249  && (clone_data->start_notify == NULL)
1250  && (clone_data->promote_notify == NULL));
1251  }
1252 
1253  common_free(rsc);
1254 }
1255 
1256 enum rsc_role_e
1257 clone_resource_state(const pcmk_resource_t * rsc, gboolean current)
1258 {
1259  enum rsc_role_e clone_role = pcmk_role_unknown;
1260  GList *gIter = rsc->children;
1261 
1262  for (; gIter != NULL; gIter = gIter->next) {
1263  pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
1264  enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
1265 
1266  if (a_role > clone_role) {
1267  clone_role = a_role;
1268  }
1269  }
1270 
1271  pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(clone_role));
1272  return clone_role;
1273 }
1274 
1282 bool
1284  const pcmk_scheduler_t *scheduler)
1285 {
1286  if (pcmk__is_clone(rsc)) {
1287  clone_variant_data_t *clone_data = rsc->variant_opaque;
1288 
1289  if (clone_data->clone_max == g_list_length(scheduler->nodes)) {
1290  return TRUE;
1291  }
1292  }
1293  return FALSE;
1294 }
1295 
1296 gboolean
1297 pe__clone_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
1298  gboolean check_parent)
1299 {
1300  gboolean passes = FALSE;
1301  clone_variant_data_t *clone_data = NULL;
1302 
1304  passes = TRUE;
1305  } else {
1306  get_clone_variant_data(clone_data, rsc);
1307  passes = pcmk__str_in_list(pcmk__xe_id(clone_data->xml_obj_child),
1308  only_rsc, pcmk__str_star_matches);
1309 
1310  if (!passes) {
1311  for (const GList *iter = rsc->children;
1312  iter != NULL; iter = iter->next) {
1313 
1314  const pcmk_resource_t *child_rsc = NULL;
1315 
1316  child_rsc = (const pcmk_resource_t *) iter->data;
1317  if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
1318  passes = TRUE;
1319  break;
1320  }
1321  }
1322  }
1323  }
1324  return !passes;
1325 }
1326 
1327 const char *
1329 {
1330  clone_variant_data_t *clone_data = NULL;
1331  get_clone_variant_data(clone_data, rsc);
1332  return pcmk__xe_id(clone_data->xml_obj_child);
1333 }
1334 
1343 bool
1345 {
1346  clone_variant_data_t *clone_data = NULL;
1347 
1348  get_clone_variant_data(clone_data, clone);
1349  return pcmk_is_set(clone_data->flags, pcmk__clone_ordered);
1350 }
1351 
1362 int
1364 {
1365  clone_variant_data_t *clone_data = NULL;
1366 
1367  get_clone_variant_data(clone_data, clone);
1368  if (pcmk_is_set(clone_data->flags, flag)) {
1369  return pcmk_rc_already;
1370  }
1371  clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
1372  "Clone", clone->id,
1373  clone_data->flags, flag, "flag");
1374  return pcmk_rc_ok;
1375 }
1376 
1386 bool
1388 {
1389  clone_variant_data_t *clone_data = NULL;
1390 
1391  get_clone_variant_data(clone_data, clone);
1392  pcmk__assert(clone_data != NULL);
1393 
1394  return pcmk_all_flags_set(clone_data->flags, flags);
1395 }
1396 
1405 void
1407  bool any_demoting)
1408 {
1409  pcmk_action_t *action = NULL;
1410  pcmk_action_t *action_complete = NULL;
1411  clone_variant_data_t *clone_data = NULL;
1412 
1413  get_clone_variant_data(clone_data, clone);
1414 
1415  // Create a "promote" action for the clone itself
1417  !any_promoting, true);
1418 
1419  // Create a "promoted" action for when all promotions are done
1420  action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTED,
1421  !any_promoting, true);
1422  action_complete->priority = PCMK_SCORE_INFINITY;
1423 
1424  // Create notification pseudo-actions for promotion
1425  if (clone_data->promote_notify == NULL) {
1426  clone_data->promote_notify = pe__action_notif_pseudo_ops(clone,
1428  action,
1429  action_complete);
1430  }
1431 
1432  // Create a "demote" action for the clone itself
1434  !any_demoting, true);
1435 
1436  // Create a "demoted" action for when all demotions are done
1437  action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTED,
1438  !any_demoting, true);
1439  action_complete->priority = PCMK_SCORE_INFINITY;
1440 
1441  // Create notification pseudo-actions for demotion
1442  if (clone_data->demote_notify == NULL) {
1443  clone_data->demote_notify = pe__action_notif_pseudo_ops(clone,
1445  action,
1446  action_complete);
1447 
1448  if (clone_data->promote_notify != NULL) {
1449  order_actions(clone_data->stop_notify->post_done,
1450  clone_data->promote_notify->pre, pcmk__ar_ordered);
1451  order_actions(clone_data->start_notify->post_done,
1452  clone_data->promote_notify->pre, pcmk__ar_ordered);
1453  order_actions(clone_data->demote_notify->post_done,
1454  clone_data->promote_notify->pre, pcmk__ar_ordered);
1455  order_actions(clone_data->demote_notify->post_done,
1456  clone_data->start_notify->pre, pcmk__ar_ordered);
1457  order_actions(clone_data->demote_notify->post_done,
1458  clone_data->stop_notify->pre, pcmk__ar_ordered);
1459  }
1460  }
1461 }
1462 
1469 void
1471 {
1472  clone_variant_data_t *clone_data = NULL;
1473 
1474  get_clone_variant_data(clone_data, clone);
1475 
1476  pe__create_action_notifications(clone, clone_data->start_notify);
1477  pe__create_action_notifications(clone, clone_data->stop_notify);
1478  pe__create_action_notifications(clone, clone_data->promote_notify);
1479  pe__create_action_notifications(clone, clone_data->demote_notify);
1480 }
1481 
1488 void
1490 {
1491  clone_variant_data_t *clone_data = NULL;
1492 
1493  get_clone_variant_data(clone_data, clone);
1494 
1495  pe__free_action_notification_data(clone_data->demote_notify);
1496  clone_data->demote_notify = NULL;
1497 
1498  pe__free_action_notification_data(clone_data->stop_notify);
1499  clone_data->stop_notify = NULL;
1500 
1501  pe__free_action_notification_data(clone_data->start_notify);
1502  clone_data->start_notify = NULL;
1503 
1504  pe__free_action_notification_data(clone_data->promote_notify);
1505  clone_data->promote_notify = NULL;
1506 }
1507 
1518 void
1520  pcmk_action_t *start, pcmk_action_t *started,
1521  pcmk_action_t *stop, pcmk_action_t *stopped)
1522 {
1523  clone_variant_data_t *clone_data = NULL;
1524 
1525  get_clone_variant_data(clone_data, clone);
1526 
1527  if (clone_data->start_notify == NULL) {
1528  clone_data->start_notify = pe__action_notif_pseudo_ops(clone,
1530  start, started);
1531  }
1532 
1533  if (clone_data->stop_notify == NULL) {
1534  clone_data->stop_notify = pe__action_notif_pseudo_ops(clone,
1536  stop, stopped);
1537  if ((clone_data->start_notify != NULL)
1538  && (clone_data->stop_notify != NULL)) {
1539  order_actions(clone_data->stop_notify->post_done,
1540  clone_data->start_notify->pre, pcmk__ar_ordered);
1541  }
1542  }
1543 }
1544 
1553 unsigned int
1555 {
1556  const clone_variant_data_t *clone_data = NULL;
1557 
1558  get_clone_variant_data(clone_data, rsc);
1559  return clone_data->clone_node_max;
1560 }
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition: complex.c:1071
bool pe__clone_is_ordered(const pcmk_resource_t *clone)
Definition: clone.c:1344
#define LOG_TRACE
Definition: logging.h:38
pcmk__cpg_host_t host
Definition: cpg.c:52
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
xmlNode * orig_xml
Definition: resources.h:403
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:974
GHashTable * known_on
Definition: resources.h:459
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition: roles.c:23
unsigned int pe__clone_max_per_node(const pcmk_resource_t *rsc)
Definition: clone.c:1554
#define PCMK_XA_MANAGED
Definition: xml_names.h:323
#define PROMOTED_INSTANCES
Definition: clone.c:27
pcmk_node_t *(* location)(const pcmk_resource_t *rsc, GList **list, int current)
Definition: resources.h:328
Control output from tools.
#define PCMK__XA_RC_CODE
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:126
int priority
Definition: actions.h:338
Stopped.
Definition: roles.h:36
bool is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any)
Definition: clone.c:588
#define PCMK_XE_PRIMITIVE
Definition: xml_names.h:164
gint pe__cmp_node_name(gconstpointer a, gconstpointer b)
Definition: utils.c:145
xmlNode * pe__failed_probe_for_rsc(const pcmk_resource_t *rsc, const char *name)
Definition: utils.c:883
bool pe__is_universal_clone(const pcmk_resource_t *rsc, const pcmk_scheduler_t *scheduler)
Definition: clone.c:1283
#define pcmk__config_warn(fmt...)
void pe__create_clone_notifications(pcmk_resource_t *clone)
Definition: clone.c:1470
GList * children
Definition: resources.h:471
#define pcmk__rsc_trace(rsc, fmt, args...)
#define UNPROMOTED_INSTANCES
Definition: clone.c:28
pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id)
Definition: status.c:435
void pe__force_anon(const char *standard, pcmk_resource_t *rsc, const char *rid, pcmk_scheduler_t *scheduler)
Definition: clone.c:212
xmlNode * xml
Definition: resources.h:400
enum rsc_role_e(* state)(const pcmk_resource_t *rsc, gboolean current)
Definition: resources.h:316
pcmk_resource_t * pe__create_clone_child(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
Definition: clone.c:247
gboolean clone_active(pcmk_resource_t *rsc, gboolean all)
Definition: clone.c:465
#define pcmk__insert_meta(obj, name, value)
#define pcmk__config_err(fmt...)
const char * pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts)
Definition: pe_output.c:22
GHashTable * meta
Definition: resources.h:467
#define PCMK_XA_FAILURE_IGNORED
Definition: xml_names.h:284
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:313
int pe__clone_default(pcmk__output_t *out, va_list args)
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition: strings.c:806
pcmk__clone_flags
#define PCMK__META_CLONE
#define PCMK_META_CLONE_MAX
Definition: options.h:82
enum crm_ais_msg_types type
Definition: cpg.c:51
const char * rsc_printable_id(const pcmk_resource_t *rsc)
Definition: utils.c:553
void pe__set_resource_flags_recursive(pcmk_resource_t *rsc, uint64_t flags)
Definition: utils.c:581
void(* print)(pcmk_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: resources.h:295
void clone_free(pcmk_resource_t *rsc)
Definition: clone.c:1223
struct clone_variant_data_s clone_variant_data_t
gboolean clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
Definition: clone.c:348
#define PCMK_XA_TARGET_ROLE
Definition: xml_names.h:422
char * pending_task
Definition: resources.h:424
int pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, pcmk_resource_t *parent, pcmk_scheduler_t *scheduler)
Definition: complex.c:691
const char * action
Definition: pcmk_fence.c:30
#define PCMK_XA_DISABLED
Definition: xml_names.h:265
enum rsc_role_e pcmk_parse_role(const char *role)
Parse a resource role from a string role specification.
Definition: roles.c:59
#define PCMK_XA_FAILED
Definition: xml_names.h:283
void clone_print(pcmk_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: clone.c:623
pcmk_action_t * pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional, bool runnable)
Definition: pe_actions.c:1804
int pe__clone_promoted_max(const pcmk_resource_t *clone)
Definition: clone.c:97
#define PCMK__META_PROMOTED_NODE_MAX_LEGACY
#define PCMK_XE_CLONE
Definition: xml_names.h:80
#define PCMK_META_GLOBALLY_UNIQUE
Definition: options.h:89
#define PCMK_ACTION_DEMOTE
Definition: actions.h:49
void common_free(pcmk_resource_t *rsc)
Definition: complex.c:1088
void(* free)(pcmk_resource_t *rsc)
Definition: resources.h:336
void pe__free_action_notification_data(notify_data_t *n_data)
Definition: pe_notif.c:956
Actions are ordered (optionally, if no other flags are set)
pcmk_node_t * pcmk__find_node_in_list(const GList *nodes, const char *node_name)
Definition: nodes.c:150
#define PCMK_META_CLONE_NODE_MAX
Definition: options.h:84
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:458
void pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting, bool any_demoting)
Definition: clone.c:1406
int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name,...) G_GNUC_NULL_TERMINATED
Definition: pe_output.c:611
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
#define PCMK_XA_DESCRIPTION
Definition: xml_names.h:261
int pe__clone_xml(pcmk__output_t *out, va_list args)
void pe__create_action_notifications(pcmk_resource_t *rsc, notify_data_t *n_data)
Definition: pe_notif.c:939
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:94
struct pe_node_shared_s * details
Definition: nodes.h:168
#define PCMK_ACTION_START
Definition: actions.h:72
#define PCMK_META_RESOURCE_STICKINESS
Definition: options.h:111
unsigned long long flags
Definition: resources.h:428
const char * uname
Definition: nodes.h:74
Unpromoted.
Definition: roles.h:38
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:34
#define PCMK_META_TARGET_ROLE
Definition: options.h:113
#define PCMK__META_PROMOTED_MAX_LEGACY
PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *", "GList *")
Definition: clone.c:850
#define PCMK_ACTION_STOP
Definition: actions.h:75
#define PCMK_XA_ID
Definition: xml_names.h:301
void free_xml(xmlNode *child)
Definition: xml.c:958
#define get_clone_variant_data(data, rsc)
Definition: clone.c:50
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1062
#define pcmk__warn_once(wo_flag, fmt...)
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:562
gboolean order_actions(pcmk_action_t *first, pcmk_action_t *then, uint32_t flags)
Definition: utils.c:457
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition: clone.c:229
#define PCMK_XA_MAINTENANCE
Definition: xml_names.h:321
enum rsc_role_e clone_resource_state(const pcmk_resource_t *rsc, gboolean current)
Definition: clone.c:1257
void pe__free_clone_notification_data(pcmk_resource_t *clone)
Definition: clone.c:1489
#define pcmk__assert(expr)
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
notify_data_t * pe__action_notif_pseudo_ops(pcmk_resource_t *rsc, const char *task, pcmk_action_t *action, pcmk_action_t *complete)
Definition: pe_notif.c:432
void pe__create_clone_notif_pseudo_ops(pcmk_resource_t *clone, pcmk_action_t *start, pcmk_action_t *started, pcmk_action_t *stop, pcmk_action_t *stopped)
Definition: clone.c:1519
gboolean(* active)(pcmk_resource_t *rsc, gboolean all)
Definition: resources.h:306
int pe__clone_node_max(const pcmk_resource_t *clone)
Definition: clone.c:80
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
Cluster status and scheduling.
int pe__clone_max(const pcmk_resource_t *clone)
Definition: clone.c:63
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:695
int pe__set_clone_flag(pcmk_resource_t *clone, enum pcmk__clone_flags flag)
Definition: clone.c:1363
pcmk_rsc_methods_t * fns
Definition: resources.h:412
void * variant_opaque
Definition: resources.h:411
#define PCMK_XA_MULTI_STATE
Definition: xml_names.h:329
pcmk_scheduler_t * scheduler
#define PCMK_META_PROMOTED_NODE_MAX
Definition: options.h:103
const char * pe__clone_child_id(const pcmk_resource_t *rsc)
Definition: clone.c:1328
#define status_print(fmt, args...)
bool pcmk__rsc_filtered_by_node(pcmk_resource_t *rsc, GList *only_node)
Definition: utils.c:789
This structure contains everything that makes up a single output formatter.
bool pe__resource_is_disabled(const pcmk_resource_t *rsc)
Definition: utils.c:737
#define PCMK_ACTION_PROMOTE
Definition: actions.h:66
#define PCMK_XE_GROUP
Definition: xml_names.h:119
#define PCMK_META_PROMOTED_MAX
Definition: options.h:102
int pcmk__numeric_strcasecmp(const char *s1, const char *s2)
Definition: strings.c:1090
gboolean(* is_filtered)(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: resources.h:358
GList * running_on
Definition: resources.h:456
bool pe__clone_flag_is_set(const pcmk_resource_t *clone, uint32_t flags)
Definition: clone.c:1387
gboolean pe__clone_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: clone.c:1297
#define PCMK_ACTION_PROMOTED
Definition: actions.h:67
gboolean crm_is_true(const char *s)
Definition: strings.c:500
int pe__clone_promoted_node_max(const pcmk_resource_t *clone)
Definition: clone.c:114
Resource role is unknown.
Definition: roles.h:35
#define PCMK_META_ORDERED
Definition: options.h:99
#define PCMK_ACTION_DEMOTED
Definition: actions.h:50
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
gboolean unclean
Definition: nodes.h:92
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition: strings.c:713
#define PCMK_XA_UNIQUE
Definition: xml_names.h:434
gboolean online
Definition: nodes.h:81
uint64_t flags
Definition: remote.c:215
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:24
GHashTable * allowed_nodes
Definition: resources.h:462
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition: strings.c:993