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