This source file includes following definitions.
- pe__clone_max
- pe__clone_node_max
- pe__clone_promoted_max
- pe__clone_promoted_node_max
- sorted_hash_table_values
- nodes_with_status
- node_list_to_str
- clone_header
- pe__force_anon
- pe__create_clone_child
- unpack_meta_int
- clone_unpack
- clone_active
- configured_role_str
- configured_role
- is_set_recursive
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- clone_free
- clone_resource_state
- pe__is_universal_clone
- pe__clone_is_filtered
- pe__clone_child_id
- pe__clone_is_ordered
- pe__set_clone_flag
- pe__clone_flag_is_set
- pe__create_promotable_pseudo_ops
- pe__create_clone_notifications
- pe__free_clone_notification_data
- pe__create_clone_notif_pseudo_ops
- pe__clone_max_per_node
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdbool.h>
13 #include <stdint.h>
14
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>
20 #include <crm/common/xml_internal.h>
21 #include <crm/common/scheduler_internal.h>
22
23 typedef struct clone_variant_data_s {
24 int clone_max;
25 int clone_node_max;
26
27 int promoted_max;
28 int promoted_node_max;
29
30 int total_clones;
31
32 uint32_t flags;
33
34 notify_data_t *stop_notify;
35 notify_data_t *start_notify;
36 notify_data_t *demote_notify;
37 notify_data_t *promote_notify;
38
39 xmlNode *xml_obj_child;
40 } clone_variant_data_t;
41
42 #define get_clone_variant_data(data, rsc) do { \
43 pcmk__assert(pcmk__is_clone(rsc)); \
44 data = rsc->priv->variant_opaque; \
45 } while (0)
46
47
48
49
50
51
52
53
54
55 int
56 pe__clone_max(const pcmk_resource_t *clone)
57 {
58 const clone_variant_data_t *clone_data = NULL;
59
60 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
61 return clone_data->clone_max;
62 }
63
64
65
66
67
68
69
70
71
72 int
73 pe__clone_node_max(const pcmk_resource_t *clone)
74 {
75 const clone_variant_data_t *clone_data = NULL;
76
77 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
78 return clone_data->clone_node_max;
79 }
80
81
82
83
84
85
86
87
88
89 int
90 pe__clone_promoted_max(const pcmk_resource_t *clone)
91 {
92 clone_variant_data_t *clone_data = NULL;
93
94 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
95 return clone_data->promoted_max;
96 }
97
98
99
100
101
102
103
104
105
106 int
107 pe__clone_promoted_node_max(const pcmk_resource_t *clone)
108 {
109 clone_variant_data_t *clone_data = NULL;
110
111 get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
112 return clone_data->promoted_node_max;
113 }
114
115 static GList *
116 sorted_hash_table_values(GHashTable *table)
117 {
118 GList *retval = NULL;
119 GHashTableIter iter;
120 gpointer key, value;
121
122 g_hash_table_iter_init(&iter, table);
123 while (g_hash_table_iter_next(&iter, &key, &value)) {
124 if (!g_list_find_custom(retval, value, (GCompareFunc) strcmp)) {
125 retval = g_list_prepend(retval, (char *) value);
126 }
127 }
128
129 retval = g_list_sort(retval, (GCompareFunc) strcmp);
130 return retval;
131 }
132
133 static GList *
134 nodes_with_status(GHashTable *table, const char *status)
135 {
136 GList *retval = NULL;
137 GHashTableIter iter;
138 gpointer key, value;
139
140 g_hash_table_iter_init(&iter, table);
141 while (g_hash_table_iter_next(&iter, &key, &value)) {
142 if (!strcmp((char *) value, status)) {
143 retval = g_list_prepend(retval, key);
144 }
145 }
146
147 retval = g_list_sort(retval, (GCompareFunc) pcmk__numeric_strcasecmp);
148 return retval;
149 }
150
151 static GString *
152 node_list_to_str(const GList *list)
153 {
154 GString *retval = NULL;
155
156 for (const GList *iter = list; iter != NULL; iter = iter->next) {
157 pcmk__add_word(&retval, 1024, (const char *) iter->data);
158 }
159
160 return retval;
161 }
162
163 static void
164 clone_header(pcmk__output_t *out, int *rc, const pcmk_resource_t *rsc,
165 clone_variant_data_t *clone_data, const char *desc)
166 {
167 GString *attrs = NULL;
168
169 if (pcmk_is_set(rsc->flags, pcmk__rsc_promotable)) {
170 pcmk__add_separated_word(&attrs, 64, "promotable", ", ");
171 }
172
173 if (pcmk_is_set(rsc->flags, pcmk__rsc_unique)) {
174 pcmk__add_separated_word(&attrs, 64, "unique", ", ");
175 }
176
177 if (pe__resource_is_disabled(rsc)) {
178 pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
179 }
180
181 if (pcmk_is_set(rsc->flags, pcmk__rsc_maintenance)) {
182 pcmk__add_separated_word(&attrs, 64, "maintenance", ", ");
183
184 } else if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
185 pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
186 }
187
188 if (attrs != NULL) {
189 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)%s%s%s",
190 rsc->id,
191 pcmk__xe_id(clone_data->xml_obj_child),
192 (const char *) attrs->str, desc ? " (" : "",
193 desc ? desc : "", desc ? ")" : "");
194 g_string_free(attrs, TRUE);
195 } else {
196 PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]%s%s%s",
197 rsc->id,
198 pcmk__xe_id(clone_data->xml_obj_child),
199 desc ? " (" : "", desc ? desc : "",
200 desc ? ")" : "");
201 }
202 }
203
204 void
205 pe__force_anon(const char *standard, pcmk_resource_t *rsc, const char *rid,
206 pcmk_scheduler_t *scheduler)
207 {
208 if (pcmk__is_clone(rsc)) {
209 clone_variant_data_t *clone_data = rsc->priv->variant_opaque;
210
211 pcmk__config_warn("Ignoring " PCMK_META_GLOBALLY_UNIQUE " for %s "
212 "because %s resources such as %s can be used only as "
213 "anonymous clones", rsc->id, standard, rid);
214
215 clone_data->clone_node_max = 1;
216 clone_data->clone_max = QB_MIN(clone_data->clone_max,
217 g_list_length(scheduler->nodes));
218 }
219 }
220
221 pcmk_resource_t *
222 pe__create_clone_child(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
223 {
224 gboolean as_orphan = FALSE;
225 char *inc_num = NULL;
226 char *inc_max = NULL;
227 pcmk_resource_t *child_rsc = NULL;
228 xmlNode *child_copy = NULL;
229 clone_variant_data_t *clone_data = NULL;
230
231 get_clone_variant_data(clone_data, rsc);
232
233 CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
234
235 if (clone_data->total_clones >= clone_data->clone_max) {
236
237 as_orphan = TRUE;
238 }
239
240
241 inc_num = pcmk__itoa(clone_data->total_clones);
242 inc_max = pcmk__itoa(clone_data->clone_max);
243
244 child_copy = pcmk__xml_copy(NULL, clone_data->xml_obj_child);
245
246 crm_xml_add(child_copy, PCMK__META_CLONE, inc_num);
247
248 if (pe__unpack_resource(child_copy, &child_rsc, rsc,
249 scheduler) != pcmk_rc_ok) {
250 goto bail;
251 }
252
253
254 pcmk__assert(child_rsc != NULL);
255 clone_data->total_clones += 1;
256 pcmk__rsc_trace(child_rsc, "Setting clone attributes for: %s",
257 child_rsc->id);
258 rsc->priv->children = g_list_append(rsc->priv->children, child_rsc);
259 if (as_orphan) {
260 pe__set_resource_flags_recursive(child_rsc, pcmk__rsc_removed);
261 }
262
263 pcmk__insert_meta(child_rsc->priv, PCMK_META_CLONE_MAX, inc_max);
264 pcmk__rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
265
266 bail:
267 free(inc_num);
268 free(inc_max);
269
270 return child_rsc;
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 static int
287 unpack_meta_int(const pcmk_resource_t *rsc, const char *meta_name,
288 const char *deprecated_name, int default_value)
289 {
290 int integer = default_value;
291 const char *value = g_hash_table_lookup(rsc->priv->meta, meta_name);
292
293 if ((value == NULL) && (deprecated_name != NULL)) {
294 value = g_hash_table_lookup(rsc->priv->meta, deprecated_name);
295
296 if (value != NULL) {
297 if (pcmk__str_eq(deprecated_name, PCMK__META_PROMOTED_MAX_LEGACY,
298 pcmk__str_none)) {
299 pcmk__warn_once(pcmk__wo_clone_master_max,
300 "Support for the " PCMK__META_PROMOTED_MAX_LEGACY
301 " meta-attribute (such as in %s) is deprecated "
302 "and will be removed in a future release. Use the "
303 PCMK_META_PROMOTED_MAX " meta-attribute instead.",
304 rsc->id);
305 } else if (pcmk__str_eq(deprecated_name, PCMK__META_PROMOTED_NODE_MAX_LEGACY,
306 pcmk__str_none)) {
307 pcmk__warn_once(pcmk__wo_clone_master_node_max,
308 "Support for the " PCMK__META_PROMOTED_NODE_MAX_LEGACY
309 " meta-attribute (such as in %s) is deprecated "
310 "and will be removed in a future release. Use the "
311 PCMK_META_PROMOTED_NODE_MAX " meta-attribute instead.",
312 rsc->id);
313 }
314 }
315 }
316 if (value != NULL) {
317 pcmk__scan_min_int(value, &integer, 0);
318 }
319 return integer;
320 }
321
322 bool
323 clone_unpack(pcmk_resource_t *rsc)
324 {
325 int lpc = 0;
326 int num_nodes = g_list_length(rsc->priv->scheduler->nodes);
327 xmlNode *a_child = NULL;
328 xmlNode *xml_obj = rsc->priv->xml;
329 clone_variant_data_t *clone_data = NULL;
330
331 pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
332
333 clone_data = pcmk__assert_alloc(1, sizeof(clone_variant_data_t));
334 rsc->priv->variant_opaque = clone_data;
335
336 if (pcmk_is_set(rsc->flags, pcmk__rsc_promotable)) {
337
338
339 clone_data->promoted_max =
340 unpack_meta_int(rsc, PCMK_META_PROMOTED_MAX,
341 PCMK__META_PROMOTED_MAX_LEGACY, 1);
342
343
344
345 clone_data->promoted_node_max =
346 unpack_meta_int(rsc, PCMK_META_PROMOTED_NODE_MAX,
347 PCMK__META_PROMOTED_NODE_MAX_LEGACY, 1);
348 }
349
350
351 clone_data->clone_node_max = unpack_meta_int(rsc, PCMK_META_CLONE_NODE_MAX,
352 NULL, 1);
353
354
355
356
357
358
359 clone_data->clone_max = unpack_meta_int(rsc, PCMK_META_CLONE_MAX, NULL,
360 QB_MAX(1, num_nodes));
361
362 if (crm_is_true(g_hash_table_lookup(rsc->priv->meta,
363 PCMK_META_ORDERED))) {
364 clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
365 "Clone", rsc->id,
366 clone_data->flags,
367 pcmk__clone_ordered,
368 "pcmk__clone_ordered");
369 }
370
371 if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)
372 && (clone_data->clone_node_max > 1)) {
373
374 pcmk__config_err("Ignoring " PCMK_META_CLONE_NODE_MAX " of %d for %s "
375 "because anonymous clones support only one instance "
376 "per node", clone_data->clone_node_max, rsc->id);
377 clone_data->clone_node_max = 1;
378 }
379
380 pcmk__rsc_trace(rsc, "Options for %s", rsc->id);
381 pcmk__rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
382 pcmk__rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
383 pcmk__rsc_trace(rsc, "\tClone is unique: %s",
384 pcmk__flag_text(rsc->flags, pcmk__rsc_unique));
385 pcmk__rsc_trace(rsc, "\tClone is promotable: %s",
386 pcmk__flag_text(rsc->flags, pcmk__rsc_promotable));
387
388
389 for (a_child = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
390 a_child != NULL; a_child = pcmk__xe_next(a_child, NULL)) {
391
392 if (pcmk__str_any_of((const char *) a_child->name,
393 PCMK_XE_PRIMITIVE, PCMK_XE_GROUP, NULL)) {
394 clone_data->xml_obj_child = a_child;
395 break;
396 }
397 }
398
399 if (clone_data->xml_obj_child == NULL) {
400 pcmk__config_err("%s has nothing to clone", rsc->id);
401 return FALSE;
402 }
403
404
405
406
407
408
409
410 if (g_hash_table_lookup(rsc->priv->meta,
411 PCMK_META_RESOURCE_STICKINESS) == NULL) {
412 pcmk__insert_meta(rsc->priv, PCMK_META_RESOURCE_STICKINESS, "1");
413 }
414
415
416
417
418
419 pcmk__insert_meta(rsc->priv, PCMK_META_GLOBALLY_UNIQUE,
420 pcmk__flag_text(rsc->flags, pcmk__rsc_unique));
421
422 if (clone_data->clone_max <= 0) {
423
424
425
426 if (pe__create_clone_child(rsc, rsc->priv->scheduler) == NULL) {
427 return FALSE;
428 }
429
430 } else {
431
432 for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
433 if (pe__create_clone_child(rsc, rsc->priv->scheduler) == NULL) {
434 return FALSE;
435 }
436 }
437 }
438
439 pcmk__rsc_trace(rsc, "Added %d children to resource %s...",
440 clone_data->clone_max, rsc->id);
441 return TRUE;
442 }
443
444 bool
445 clone_active(const pcmk_resource_t *rsc, bool all)
446 {
447 for (GList *gIter = rsc->priv->children;
448 gIter != NULL; gIter = gIter->next) {
449
450 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
451 bool child_active = child_rsc->priv->fns->active(child_rsc, all);
452
453 if (all == FALSE && child_active) {
454 return TRUE;
455 } else if (all && child_active == FALSE) {
456 return FALSE;
457 }
458 }
459
460 if (all) {
461 return TRUE;
462 } else {
463 return FALSE;
464 }
465 }
466
467 static const char *
468 configured_role_str(pcmk_resource_t * rsc)
469 {
470 const char *target_role = g_hash_table_lookup(rsc->priv->meta,
471 PCMK_META_TARGET_ROLE);
472
473 if ((target_role == NULL) && (rsc->priv->children != NULL)) {
474
475 pcmk_resource_t *instance = rsc->priv->children->data;
476
477 target_role = g_hash_table_lookup(instance->priv->meta,
478 PCMK_META_TARGET_ROLE);
479 }
480 return target_role;
481 }
482
483 static enum rsc_role_e
484 configured_role(pcmk_resource_t *rsc)
485 {
486 enum rsc_role_e role = pcmk_role_unknown;
487 const char *target_role = configured_role_str(rsc);
488
489 if (target_role != NULL) {
490 role = pcmk_parse_role(target_role);
491 if (role == pcmk_role_unknown) {
492 pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
493 " for resource %s", rsc->id);
494 }
495 }
496 return role;
497 }
498
499 bool
500 is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any)
501 {
502 bool all = !any;
503
504 if (pcmk_is_set(rsc->flags, flag)) {
505 if(any) {
506 return TRUE;
507 }
508 } else if(all) {
509 return FALSE;
510 }
511
512 for (GList *gIter = rsc->priv->children;
513 gIter != NULL; gIter = gIter->next) {
514
515 if(is_set_recursive(gIter->data, flag, any)) {
516 if(any) {
517 return TRUE;
518 }
519
520 } else if(all) {
521 return FALSE;
522 }
523 }
524
525 if(all) {
526 return TRUE;
527 }
528 return FALSE;
529 }
530
531 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
532 "GList *")
533 int
534 pe__clone_xml(pcmk__output_t *out, va_list args)
535 {
536 uint32_t show_opts = va_arg(args, uint32_t);
537 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
538 GList *only_node = va_arg(args, GList *);
539 GList *only_rsc = va_arg(args, GList *);
540
541 GList *all = NULL;
542 int rc = pcmk_rc_no_output;
543 gboolean printed_header = FALSE;
544 bool print_everything = true;
545
546 if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
547 return rc;
548 }
549
550 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
551 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
552
553 all = g_list_prepend(all, (gpointer) "*");
554
555 for (GList *gIter = rsc->priv->children;
556 gIter != NULL; gIter = gIter->next) {
557
558 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
559
560 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
561 continue;
562 }
563
564 if (child_rsc->priv->fns->is_filtered(child_rsc, only_rsc,
565 print_everything)) {
566 continue;
567 }
568
569 if (!printed_header) {
570 const char *multi_state = pcmk__flag_text(rsc->flags,
571 pcmk__rsc_promotable);
572 const char *unique = pcmk__flag_text(rsc->flags, pcmk__rsc_unique);
573 const char *maintenance = pcmk__flag_text(rsc->flags,
574 pcmk__rsc_maintenance);
575 const char *managed = pcmk__flag_text(rsc->flags,
576 pcmk__rsc_managed);
577 const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc));
578 const char *failed = pcmk__flag_text(rsc->flags, pcmk__rsc_failed);
579 const char *ignored = pcmk__flag_text(rsc->flags,
580 pcmk__rsc_ignore_failure);
581 const char *target_role = configured_role_str(rsc);
582 const char *desc = pe__resource_description(rsc, show_opts);
583
584 printed_header = TRUE;
585
586 rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_CLONE,
587 PCMK_XA_ID, rsc->id,
588 PCMK_XA_MULTI_STATE, multi_state,
589 PCMK_XA_UNIQUE, unique,
590 PCMK_XA_MAINTENANCE, maintenance,
591 PCMK_XA_MANAGED, managed,
592 PCMK_XA_DISABLED, disabled,
593 PCMK_XA_FAILED, failed,
594 PCMK_XA_FAILURE_IGNORED, ignored,
595 PCMK_XA_TARGET_ROLE, target_role,
596 PCMK_XA_DESCRIPTION, desc,
597 NULL);
598 pcmk__assert(rc == pcmk_rc_ok);
599 }
600
601 out->message(out, (const char *) child_rsc->priv->xml->name,
602 show_opts, child_rsc, only_node, all);
603 }
604
605 if (printed_header) {
606 pcmk__output_xml_pop_parent(out);
607 }
608
609 g_list_free(all);
610 return rc;
611 }
612
613 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
614 "GList *")
615 int
616 pe__clone_default(pcmk__output_t *out, va_list args)
617 {
618 uint32_t show_opts = va_arg(args, uint32_t);
619 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
620 GList *only_node = va_arg(args, GList *);
621 GList *only_rsc = va_arg(args, GList *);
622
623 GHashTable *stopped = NULL;
624
625 GString *list_text = NULL;
626
627 GList *promoted_list = NULL;
628 GList *started_list = NULL;
629 GList *gIter = NULL;
630
631 const char *desc = NULL;
632
633 clone_variant_data_t *clone_data = NULL;
634 int active_instances = 0;
635 int rc = pcmk_rc_no_output;
636 gboolean print_everything = TRUE;
637
638 desc = pe__resource_description(rsc, show_opts);
639
640 get_clone_variant_data(clone_data, rsc);
641
642 if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
643 return rc;
644 }
645
646 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
647 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
648
649 for (gIter = rsc->priv->children; gIter != NULL; gIter = gIter->next) {
650 gboolean print_full = FALSE;
651 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
652 bool partially_active = child_rsc->priv->fns->active(child_rsc, false);
653
654 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
655 continue;
656 }
657
658 if (child_rsc->priv->fns->is_filtered(child_rsc, only_rsc,
659 print_everything)) {
660 continue;
661 }
662
663 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
664 print_full = TRUE;
665 }
666
667 if (pcmk_is_set(rsc->flags, pcmk__rsc_unique)) {
668
669 if (partially_active
670 || !pcmk_is_set(rsc->flags, pcmk__rsc_removed)) {
671 print_full = TRUE;
672 }
673
674
675
676 } else if (pcmk_is_set(show_opts, pcmk_show_pending)
677 && (child_rsc->priv->pending_action != NULL)
678 && (strcmp(child_rsc->priv->pending_action,
679 "probe") != 0)) {
680
681 print_full = TRUE;
682
683 } else if (partially_active == FALSE) {
684
685 if (!pcmk_is_set(child_rsc->flags, pcmk__rsc_removed)
686 && !pcmk_is_set(show_opts, pcmk_show_clone_detail)
687 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
688 if (stopped == NULL) {
689 stopped = pcmk__strkey_table(free, free);
690 }
691 pcmk__insert_dup(stopped, child_rsc->id, "Stopped");
692 }
693
694 } else if (is_set_recursive(child_rsc, pcmk__rsc_removed, TRUE)
695 || !is_set_recursive(child_rsc, pcmk__rsc_managed, FALSE)
696 || is_set_recursive(child_rsc, pcmk__rsc_failed, TRUE)) {
697
698
699 print_full = TRUE;
700
701 } else if (child_rsc->priv->fns->active(child_rsc, true)) {
702
703
704 pcmk_node_t *location = NULL;
705
706 location = child_rsc->priv->fns->location(child_rsc, NULL,
707 pcmk__rsc_node_current);
708 if (location) {
709
710
711 enum rsc_role_e a_role;
712
713 a_role = child_rsc->priv->fns->state(child_rsc, TRUE);
714
715 if (location->details->online == FALSE && location->details->unclean) {
716 print_full = TRUE;
717
718 } else if (a_role > pcmk_role_unpromoted) {
719 promoted_list = g_list_append(promoted_list, location);
720
721 } else {
722 started_list = g_list_append(started_list, location);
723 }
724
725 } else {
726
727 print_full = TRUE;
728 }
729
730 } else {
731
732 print_full = TRUE;
733 }
734
735 if (print_full) {
736 GList *all = NULL;
737
738 clone_header(out, &rc, rsc, clone_data, desc);
739
740
741 all = g_list_prepend(all, (gpointer) "*");
742 out->message(out, (const char *) child_rsc->priv->xml->name,
743 show_opts, child_rsc, only_node, all);
744 g_list_free(all);
745 }
746 }
747
748 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
749 PCMK__OUTPUT_LIST_FOOTER(out, rc);
750 return pcmk_rc_ok;
751 }
752
753
754 promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
755 for (gIter = promoted_list; gIter; gIter = gIter->next) {
756 pcmk_node_t *host = gIter->data;
757
758 if (!pcmk__str_in_list(host->priv->name, only_node,
759 pcmk__str_star_matches|pcmk__str_casei)) {
760 continue;
761 }
762
763 pcmk__add_word(&list_text, 1024, host->priv->name);
764 active_instances++;
765 }
766 g_list_free(promoted_list);
767
768 if ((list_text != NULL) && (list_text->len > 0)) {
769 clone_header(out, &rc, rsc, clone_data, desc);
770
771 out->list_item(out, NULL, PCMK_ROLE_PROMOTED ": [ %s ]",
772 (const char *) list_text->str);
773 g_string_truncate(list_text, 0);
774 }
775
776
777 started_list = g_list_sort(started_list, pe__cmp_node_name);
778 for (gIter = started_list; gIter; gIter = gIter->next) {
779 pcmk_node_t *host = gIter->data;
780
781 if (!pcmk__str_in_list(host->priv->name, only_node,
782 pcmk__str_star_matches|pcmk__str_casei)) {
783 continue;
784 }
785
786 pcmk__add_word(&list_text, 1024, host->priv->name);
787 active_instances++;
788 }
789 g_list_free(started_list);
790
791 if ((list_text != NULL) && (list_text->len > 0)) {
792 clone_header(out, &rc, rsc, clone_data, desc);
793
794 if (pcmk_is_set(rsc->flags, pcmk__rsc_promotable)) {
795 enum rsc_role_e role = configured_role(rsc);
796
797 if (role == pcmk_role_unpromoted) {
798 out->list_item(out, NULL,
799 PCMK_ROLE_UNPROMOTED
800 " (" PCMK_META_TARGET_ROLE "): [ %s ]",
801 (const char *) list_text->str);
802 } else {
803 out->list_item(out, NULL, PCMK_ROLE_UNPROMOTED ": [ %s ]",
804 (const char *) list_text->str);
805 }
806
807 } else {
808 out->list_item(out, NULL, "Started: [ %s ]",
809 (const char *) list_text->str);
810 }
811 }
812
813 if (list_text != NULL) {
814 g_string_free(list_text, TRUE);
815 }
816
817 if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
818 if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)
819 && (clone_data->clone_max > active_instances)) {
820
821 GList *nIter;
822 GList *list = g_hash_table_get_values(rsc->priv->allowed_nodes);
823
824
825 if (stopped != NULL) {
826 g_hash_table_destroy(stopped);
827 stopped = NULL;
828 }
829
830 if (list == NULL) {
831
832
833
834
835 list = g_hash_table_get_values(rsc->priv->probed_nodes);
836 }
837
838 list = g_list_sort(list, pe__cmp_node_name);
839 for (nIter = list; nIter != NULL; nIter = nIter->next) {
840 pcmk_node_t *node = (pcmk_node_t *) nIter->data;
841
842 if ((pcmk__find_node_in_list(rsc->priv->active_nodes,
843 node->priv->name) == NULL)
844 && pcmk__str_in_list(node->priv->name, only_node,
845 pcmk__str_star_matches|pcmk__str_casei)) {
846
847 xmlNode *probe_op = NULL;
848 const char *state = "Stopped";
849
850 if (configured_role(rsc) == pcmk_role_stopped) {
851 state = "Stopped (disabled)";
852 }
853
854 if (stopped == NULL) {
855 stopped = pcmk__strkey_table(free, free);
856 }
857
858 probe_op = pe__failed_probe_for_rsc(rsc,
859 node->priv->name);
860 if (probe_op != NULL) {
861 int rc;
862
863 pcmk__scan_min_int(crm_element_value(probe_op,
864 PCMK__XA_RC_CODE),
865 &rc, 0);
866 g_hash_table_insert(stopped, strdup(node->priv->name),
867 crm_strdup_printf("Stopped (%s)",
868 crm_exit_str(rc)));
869 } else {
870 pcmk__insert_dup(stopped, node->priv->name, state);
871 }
872 }
873 }
874 g_list_free(list);
875 }
876
877 if (stopped != NULL) {
878 GList *list = sorted_hash_table_values(stopped);
879
880 clone_header(out, &rc, rsc, clone_data, desc);
881
882 for (GList *status_iter = list; status_iter != NULL; status_iter = status_iter->next) {
883 const char *status = status_iter->data;
884 GList *nodes = nodes_with_status(stopped, status);
885 GString *nodes_str = node_list_to_str(nodes);
886
887 if (nodes_str != NULL) {
888 if (nodes_str->len > 0) {
889 out->list_item(out, NULL, "%s: [ %s ]", status,
890 (const char *) nodes_str->str);
891 }
892 g_string_free(nodes_str, TRUE);
893 }
894
895 g_list_free(nodes);
896 }
897
898 g_list_free(list);
899 g_hash_table_destroy(stopped);
900
901
902
903
904
905 } else if (active_instances == 0) {
906 clone_header(out, &rc, rsc, clone_data, desc);
907 PCMK__OUTPUT_LIST_FOOTER(out, rc);
908 return rc;
909 }
910 }
911
912 PCMK__OUTPUT_LIST_FOOTER(out, rc);
913 return rc;
914 }
915
916 void
917 clone_free(pcmk_resource_t * rsc)
918 {
919 clone_variant_data_t *clone_data = NULL;
920
921 get_clone_variant_data(clone_data, rsc);
922
923 pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
924
925 for (GList *gIter = rsc->priv->children;
926 gIter != NULL; gIter = gIter->next) {
927
928 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
929
930 pcmk__assert(child_rsc != NULL);
931 pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
932 pcmk__xml_free(child_rsc->priv->xml);
933 child_rsc->priv->xml = NULL;
934
935 pcmk__xml_free(child_rsc->priv->orig_xml);
936 child_rsc->priv->orig_xml = NULL;
937 pcmk__free_resource(child_rsc);
938 }
939
940 g_list_free(rsc->priv->children);
941
942 if (clone_data) {
943 pcmk__assert((clone_data->demote_notify == NULL)
944 && (clone_data->stop_notify == NULL)
945 && (clone_data->start_notify == NULL)
946 && (clone_data->promote_notify == NULL));
947 }
948
949 common_free(rsc);
950 }
951
952 enum rsc_role_e
953 clone_resource_state(const pcmk_resource_t *rsc, bool current)
954 {
955 enum rsc_role_e clone_role = pcmk_role_unknown;
956
957 for (GList *gIter = rsc->priv->children;
958 gIter != NULL; gIter = gIter->next) {
959
960 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
961 enum rsc_role_e a_role = child_rsc->priv->fns->state(child_rsc,
962 current);
963
964 if (a_role > clone_role) {
965 clone_role = a_role;
966 }
967 }
968
969 pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(clone_role));
970 return clone_role;
971 }
972
973
974
975
976
977
978
979
980 bool
981 pe__is_universal_clone(const pcmk_resource_t *rsc,
982 const pcmk_scheduler_t *scheduler)
983 {
984 if (pcmk__is_clone(rsc)) {
985 clone_variant_data_t *clone_data = rsc->priv->variant_opaque;
986
987 if (clone_data->clone_max == g_list_length(scheduler->nodes)) {
988 return TRUE;
989 }
990 }
991 return FALSE;
992 }
993
994 bool
995 pe__clone_is_filtered(const pcmk_resource_t *rsc, const GList *only_rsc,
996 bool check_parent)
997 {
998 bool passes = FALSE;
999 clone_variant_data_t *clone_data = NULL;
1000
1001 if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
1002 passes = true;
1003 } else {
1004 get_clone_variant_data(clone_data, rsc);
1005 passes = pcmk__str_in_list(pcmk__xe_id(clone_data->xml_obj_child),
1006 only_rsc, pcmk__str_star_matches);
1007
1008 if (!passes) {
1009 for (const GList *iter = rsc->priv->children;
1010 iter != NULL; iter = iter->next) {
1011
1012 const pcmk_resource_t *child_rsc = NULL;
1013
1014 child_rsc = (const pcmk_resource_t *) iter->data;
1015 if (!child_rsc->priv->fns->is_filtered(child_rsc, only_rsc,
1016 false)) {
1017 passes = true;
1018 break;
1019 }
1020 }
1021 }
1022 }
1023 return !passes;
1024 }
1025
1026 const char *
1027 pe__clone_child_id(const pcmk_resource_t *rsc)
1028 {
1029 clone_variant_data_t *clone_data = NULL;
1030 get_clone_variant_data(clone_data, rsc);
1031 return pcmk__xe_id(clone_data->xml_obj_child);
1032 }
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042 bool
1043 pe__clone_is_ordered(const pcmk_resource_t *clone)
1044 {
1045 clone_variant_data_t *clone_data = NULL;
1046
1047 get_clone_variant_data(clone_data, clone);
1048 return pcmk_is_set(clone_data->flags, pcmk__clone_ordered);
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061 int
1062 pe__set_clone_flag(pcmk_resource_t *clone, enum pcmk__clone_flags flag)
1063 {
1064 clone_variant_data_t *clone_data = NULL;
1065
1066 get_clone_variant_data(clone_data, clone);
1067 if (pcmk_is_set(clone_data->flags, flag)) {
1068 return pcmk_rc_already;
1069 }
1070 clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
1071 "Clone", clone->id,
1072 clone_data->flags, flag, "flag");
1073 return pcmk_rc_ok;
1074 }
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085 bool
1086 pe__clone_flag_is_set(const pcmk_resource_t *clone, uint32_t flags)
1087 {
1088 clone_variant_data_t *clone_data = NULL;
1089
1090 get_clone_variant_data(clone_data, clone);
1091 pcmk__assert(clone_data != NULL);
1092
1093 return pcmk_all_flags_set(clone_data->flags, flags);
1094 }
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104 void
1105 pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting,
1106 bool any_demoting)
1107 {
1108 pcmk_action_t *action = NULL;
1109 pcmk_action_t *action_complete = NULL;
1110 clone_variant_data_t *clone_data = NULL;
1111
1112 get_clone_variant_data(clone_data, clone);
1113
1114
1115 action = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTE,
1116 !any_promoting, true);
1117
1118
1119 action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTED,
1120 !any_promoting, true);
1121 action_complete->priority = PCMK_SCORE_INFINITY;
1122
1123
1124 if (clone_data->promote_notify == NULL) {
1125 clone_data->promote_notify = pe__action_notif_pseudo_ops(clone,
1126 PCMK_ACTION_PROMOTE,
1127 action,
1128 action_complete);
1129 }
1130
1131
1132 action = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTE,
1133 !any_demoting, true);
1134
1135
1136 action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTED,
1137 !any_demoting, true);
1138 action_complete->priority = PCMK_SCORE_INFINITY;
1139
1140
1141 if (clone_data->demote_notify == NULL) {
1142 clone_data->demote_notify = pe__action_notif_pseudo_ops(clone,
1143 PCMK_ACTION_DEMOTE,
1144 action,
1145 action_complete);
1146
1147 if (clone_data->promote_notify != NULL) {
1148 order_actions(clone_data->stop_notify->post_done,
1149 clone_data->promote_notify->pre, pcmk__ar_ordered);
1150 order_actions(clone_data->start_notify->post_done,
1151 clone_data->promote_notify->pre, pcmk__ar_ordered);
1152 order_actions(clone_data->demote_notify->post_done,
1153 clone_data->promote_notify->pre, pcmk__ar_ordered);
1154 order_actions(clone_data->demote_notify->post_done,
1155 clone_data->start_notify->pre, pcmk__ar_ordered);
1156 order_actions(clone_data->demote_notify->post_done,
1157 clone_data->stop_notify->pre, pcmk__ar_ordered);
1158 }
1159 }
1160 }
1161
1162
1163
1164
1165
1166
1167
1168 void
1169 pe__create_clone_notifications(pcmk_resource_t *clone)
1170 {
1171 clone_variant_data_t *clone_data = NULL;
1172
1173 get_clone_variant_data(clone_data, clone);
1174
1175 pe__create_action_notifications(clone, clone_data->start_notify);
1176 pe__create_action_notifications(clone, clone_data->stop_notify);
1177 pe__create_action_notifications(clone, clone_data->promote_notify);
1178 pe__create_action_notifications(clone, clone_data->demote_notify);
1179 }
1180
1181
1182
1183
1184
1185
1186
1187 void
1188 pe__free_clone_notification_data(pcmk_resource_t *clone)
1189 {
1190 clone_variant_data_t *clone_data = NULL;
1191
1192 get_clone_variant_data(clone_data, clone);
1193
1194 pe__free_action_notification_data(clone_data->demote_notify);
1195 clone_data->demote_notify = NULL;
1196
1197 pe__free_action_notification_data(clone_data->stop_notify);
1198 clone_data->stop_notify = NULL;
1199
1200 pe__free_action_notification_data(clone_data->start_notify);
1201 clone_data->start_notify = NULL;
1202
1203 pe__free_action_notification_data(clone_data->promote_notify);
1204 clone_data->promote_notify = NULL;
1205 }
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217 void
1218 pe__create_clone_notif_pseudo_ops(pcmk_resource_t *clone,
1219 pcmk_action_t *start, pcmk_action_t *started,
1220 pcmk_action_t *stop, pcmk_action_t *stopped)
1221 {
1222 clone_variant_data_t *clone_data = NULL;
1223
1224 get_clone_variant_data(clone_data, clone);
1225
1226 if (clone_data->start_notify == NULL) {
1227 clone_data->start_notify = pe__action_notif_pseudo_ops(clone,
1228 PCMK_ACTION_START,
1229 start, started);
1230 }
1231
1232 if (clone_data->stop_notify == NULL) {
1233 clone_data->stop_notify = pe__action_notif_pseudo_ops(clone,
1234 PCMK_ACTION_STOP,
1235 stop, stopped);
1236 if ((clone_data->start_notify != NULL)
1237 && (clone_data->stop_notify != NULL)) {
1238 order_actions(clone_data->stop_notify->post_done,
1239 clone_data->start_notify->pre, pcmk__ar_ordered);
1240 }
1241 }
1242 }
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252 unsigned int
1253 pe__clone_max_per_node(const pcmk_resource_t *rsc)
1254 {
1255 const clone_variant_data_t *clone_data = NULL;
1256
1257 get_clone_variant_data(clone_data, rsc);
1258 return clone_data->clone_node_max;
1259 }