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 <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>
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 gboolean
323 clone_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
324 {
325 int lpc = 0;
326 xmlNode *a_child = NULL;
327 xmlNode *xml_obj = rsc->priv->xml;
328 clone_variant_data_t *clone_data = NULL;
329
330 pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
331
332 clone_data = pcmk__assert_alloc(1, sizeof(clone_variant_data_t));
333 rsc->priv->variant_opaque = clone_data;
334
335 if (pcmk_is_set(rsc->flags, pcmk__rsc_promotable)) {
336
337
338 clone_data->promoted_max =
339 unpack_meta_int(rsc, PCMK_META_PROMOTED_MAX,
340 PCMK__META_PROMOTED_MAX_LEGACY, 1);
341
342
343
344 clone_data->promoted_node_max =
345 unpack_meta_int(rsc, PCMK_META_PROMOTED_NODE_MAX,
346 PCMK__META_PROMOTED_NODE_MAX_LEGACY, 1);
347 }
348
349
350 clone_data->clone_node_max = unpack_meta_int(rsc, PCMK_META_CLONE_NODE_MAX,
351 NULL, 1);
352
353
354
355
356 clone_data->clone_max = unpack_meta_int(rsc, PCMK_META_CLONE_MAX, NULL,
357 QB_MAX(1, g_list_length(scheduler->nodes)));
358
359 if (crm_is_true(g_hash_table_lookup(rsc->priv->meta,
360 PCMK_META_ORDERED))) {
361 clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
362 "Clone", rsc->id,
363 clone_data->flags,
364 pcmk__clone_ordered,
365 "pcmk__clone_ordered");
366 }
367
368 if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)
369 && (clone_data->clone_node_max > 1)) {
370
371 pcmk__config_err("Ignoring " PCMK_META_CLONE_NODE_MAX " of %d for %s "
372 "because anonymous clones support only one instance "
373 "per node", clone_data->clone_node_max, rsc->id);
374 clone_data->clone_node_max = 1;
375 }
376
377 pcmk__rsc_trace(rsc, "Options for %s", rsc->id);
378 pcmk__rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
379 pcmk__rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
380 pcmk__rsc_trace(rsc, "\tClone is unique: %s",
381 pcmk__flag_text(rsc->flags, pcmk__rsc_unique));
382 pcmk__rsc_trace(rsc, "\tClone is promotable: %s",
383 pcmk__flag_text(rsc->flags, pcmk__rsc_promotable));
384
385
386 for (a_child = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
387 a_child != NULL; a_child = pcmk__xe_next(a_child, NULL)) {
388
389 if (pcmk__str_any_of((const char *) a_child->name,
390 PCMK_XE_PRIMITIVE, PCMK_XE_GROUP, NULL)) {
391 clone_data->xml_obj_child = a_child;
392 break;
393 }
394 }
395
396 if (clone_data->xml_obj_child == NULL) {
397 pcmk__config_err("%s has nothing to clone", rsc->id);
398 return FALSE;
399 }
400
401
402
403
404
405
406
407 if (g_hash_table_lookup(rsc->priv->meta,
408 PCMK_META_RESOURCE_STICKINESS) == NULL) {
409 pcmk__insert_meta(rsc->priv, PCMK_META_RESOURCE_STICKINESS, "1");
410 }
411
412
413
414
415
416 pcmk__insert_meta(rsc->priv, PCMK_META_GLOBALLY_UNIQUE,
417 pcmk__flag_text(rsc->flags, pcmk__rsc_unique));
418
419 if (clone_data->clone_max <= 0) {
420
421
422
423 if (pe__create_clone_child(rsc, scheduler) == NULL) {
424 return FALSE;
425 }
426
427 } else {
428
429 for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
430 if (pe__create_clone_child(rsc, scheduler) == NULL) {
431 return FALSE;
432 }
433 }
434 }
435
436 pcmk__rsc_trace(rsc, "Added %d children to resource %s...",
437 clone_data->clone_max, rsc->id);
438 return TRUE;
439 }
440
441 gboolean
442 clone_active(pcmk_resource_t * rsc, gboolean all)
443 {
444 for (GList *gIter = rsc->priv->children;
445 gIter != NULL; gIter = gIter->next) {
446
447 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
448 gboolean child_active = child_rsc->priv->fns->active(child_rsc, all);
449
450 if (all == FALSE && child_active) {
451 return TRUE;
452 } else if (all && child_active == FALSE) {
453 return FALSE;
454 }
455 }
456
457 if (all) {
458 return TRUE;
459 } else {
460 return FALSE;
461 }
462 }
463
464 static const char *
465 configured_role_str(pcmk_resource_t * rsc)
466 {
467 const char *target_role = g_hash_table_lookup(rsc->priv->meta,
468 PCMK_META_TARGET_ROLE);
469
470 if ((target_role == NULL) && (rsc->priv->children != NULL)) {
471
472 pcmk_resource_t *instance = rsc->priv->children->data;
473
474 target_role = g_hash_table_lookup(instance->priv->meta,
475 PCMK_META_TARGET_ROLE);
476 }
477 return target_role;
478 }
479
480 static enum rsc_role_e
481 configured_role(pcmk_resource_t *rsc)
482 {
483 enum rsc_role_e role = pcmk_role_unknown;
484 const char *target_role = configured_role_str(rsc);
485
486 if (target_role != NULL) {
487 role = pcmk_parse_role(target_role);
488 if (role == pcmk_role_unknown) {
489 pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
490 " for resource %s", rsc->id);
491 }
492 }
493 return role;
494 }
495
496 bool
497 is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any)
498 {
499 bool all = !any;
500
501 if (pcmk_is_set(rsc->flags, flag)) {
502 if(any) {
503 return TRUE;
504 }
505 } else if(all) {
506 return FALSE;
507 }
508
509 for (GList *gIter = rsc->priv->children;
510 gIter != NULL; gIter = gIter->next) {
511
512 if(is_set_recursive(gIter->data, flag, any)) {
513 if(any) {
514 return TRUE;
515 }
516
517 } else if(all) {
518 return FALSE;
519 }
520 }
521
522 if(all) {
523 return TRUE;
524 }
525 return FALSE;
526 }
527
528 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
529 "GList *")
530 int
531 pe__clone_xml(pcmk__output_t *out, va_list args)
532 {
533 uint32_t show_opts = va_arg(args, uint32_t);
534 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
535 GList *only_node = va_arg(args, GList *);
536 GList *only_rsc = va_arg(args, GList *);
537
538 GList *all = NULL;
539 int rc = pcmk_rc_no_output;
540 gboolean printed_header = FALSE;
541 gboolean print_everything = TRUE;
542
543 if (rsc->priv->fns->is_filtered(rsc, only_rsc, TRUE)) {
544 return rc;
545 }
546
547 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
548 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
549
550 all = g_list_prepend(all, (gpointer) "*");
551
552 for (GList *gIter = rsc->priv->children;
553 gIter != NULL; gIter = gIter->next) {
554
555 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
556
557 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
558 continue;
559 }
560
561 if (child_rsc->priv->fns->is_filtered(child_rsc, only_rsc,
562 print_everything)) {
563 continue;
564 }
565
566 if (!printed_header) {
567 const char *multi_state = pcmk__flag_text(rsc->flags,
568 pcmk__rsc_promotable);
569 const char *unique = pcmk__flag_text(rsc->flags, pcmk__rsc_unique);
570 const char *maintenance = pcmk__flag_text(rsc->flags,
571 pcmk__rsc_maintenance);
572 const char *managed = pcmk__flag_text(rsc->flags,
573 pcmk__rsc_managed);
574 const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc));
575 const char *failed = pcmk__flag_text(rsc->flags, pcmk__rsc_failed);
576 const char *ignored = pcmk__flag_text(rsc->flags,
577 pcmk__rsc_ignore_failure);
578 const char *target_role = configured_role_str(rsc);
579 const char *desc = pe__resource_description(rsc, show_opts);
580
581 printed_header = TRUE;
582
583 rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_CLONE,
584 PCMK_XA_ID, rsc->id,
585 PCMK_XA_MULTI_STATE, multi_state,
586 PCMK_XA_UNIQUE, unique,
587 PCMK_XA_MAINTENANCE, maintenance,
588 PCMK_XA_MANAGED, managed,
589 PCMK_XA_DISABLED, disabled,
590 PCMK_XA_FAILED, failed,
591 PCMK_XA_FAILURE_IGNORED, ignored,
592 PCMK_XA_TARGET_ROLE, target_role,
593 PCMK_XA_DESCRIPTION, desc,
594 NULL);
595 pcmk__assert(rc == pcmk_rc_ok);
596 }
597
598 out->message(out, (const char *) child_rsc->priv->xml->name,
599 show_opts, child_rsc, only_node, all);
600 }
601
602 if (printed_header) {
603 pcmk__output_xml_pop_parent(out);
604 }
605
606 g_list_free(all);
607 return rc;
608 }
609
610 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pcmk_resource_t *", "GList *",
611 "GList *")
612 int
613 pe__clone_default(pcmk__output_t *out, va_list args)
614 {
615 uint32_t show_opts = va_arg(args, uint32_t);
616 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
617 GList *only_node = va_arg(args, GList *);
618 GList *only_rsc = va_arg(args, GList *);
619
620 GHashTable *stopped = NULL;
621
622 GString *list_text = NULL;
623
624 GList *promoted_list = NULL;
625 GList *started_list = NULL;
626 GList *gIter = NULL;
627
628 const char *desc = NULL;
629
630 clone_variant_data_t *clone_data = NULL;
631 int active_instances = 0;
632 int rc = pcmk_rc_no_output;
633 gboolean print_everything = TRUE;
634
635 desc = pe__resource_description(rsc, show_opts);
636
637 get_clone_variant_data(clone_data, rsc);
638
639 if (rsc->priv->fns->is_filtered(rsc, only_rsc, TRUE)) {
640 return rc;
641 }
642
643 print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
644 (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
645
646 for (gIter = rsc->priv->children; gIter != NULL; gIter = gIter->next) {
647 gboolean print_full = FALSE;
648 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
649 gboolean partially_active = child_rsc->priv->fns->active(child_rsc,
650 FALSE);
651
652 if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
653 continue;
654 }
655
656 if (child_rsc->priv->fns->is_filtered(child_rsc, only_rsc,
657 print_everything)) {
658 continue;
659 }
660
661 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
662 print_full = TRUE;
663 }
664
665 if (pcmk_is_set(rsc->flags, pcmk__rsc_unique)) {
666
667 if (partially_active
668 || !pcmk_is_set(rsc->flags, pcmk__rsc_removed)) {
669 print_full = TRUE;
670 }
671
672
673
674 } else if (pcmk_is_set(show_opts, pcmk_show_pending)
675 && (child_rsc->priv->pending_action != NULL)
676 && (strcmp(child_rsc->priv->pending_action,
677 "probe") != 0)) {
678
679 print_full = TRUE;
680
681 } else if (partially_active == FALSE) {
682
683 if (!pcmk_is_set(child_rsc->flags, pcmk__rsc_removed)
684 && !pcmk_is_set(show_opts, pcmk_show_clone_detail)
685 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
686 if (stopped == NULL) {
687 stopped = pcmk__strkey_table(free, free);
688 }
689 pcmk__insert_dup(stopped, child_rsc->id, "Stopped");
690 }
691
692 } else if (is_set_recursive(child_rsc, pcmk__rsc_removed, TRUE)
693 || !is_set_recursive(child_rsc, pcmk__rsc_managed, FALSE)
694 || is_set_recursive(child_rsc, pcmk__rsc_failed, TRUE)) {
695
696
697 print_full = TRUE;
698
699 } else if (child_rsc->priv->fns->active(child_rsc, TRUE)) {
700
701
702 pcmk_node_t *location = NULL;
703
704 location = child_rsc->priv->fns->location(child_rsc, NULL,
705 pcmk__rsc_node_current);
706 if (location) {
707
708
709 enum rsc_role_e a_role;
710
711 a_role = child_rsc->priv->fns->state(child_rsc, TRUE);
712
713 if (location->details->online == FALSE && location->details->unclean) {
714 print_full = TRUE;
715
716 } else if (a_role > pcmk_role_unpromoted) {
717 promoted_list = g_list_append(promoted_list, location);
718
719 } else {
720 started_list = g_list_append(started_list, location);
721 }
722
723 } else {
724
725 print_full = TRUE;
726 }
727
728 } else {
729
730 print_full = TRUE;
731 }
732
733 if (print_full) {
734 GList *all = NULL;
735
736 clone_header(out, &rc, rsc, clone_data, desc);
737
738
739 all = g_list_prepend(all, (gpointer) "*");
740 out->message(out, (const char *) child_rsc->priv->xml->name,
741 show_opts, child_rsc, only_node, all);
742 g_list_free(all);
743 }
744 }
745
746 if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
747 PCMK__OUTPUT_LIST_FOOTER(out, rc);
748 return pcmk_rc_ok;
749 }
750
751
752 promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
753 for (gIter = promoted_list; gIter; gIter = gIter->next) {
754 pcmk_node_t *host = gIter->data;
755
756 if (!pcmk__str_in_list(host->priv->name, only_node,
757 pcmk__str_star_matches|pcmk__str_casei)) {
758 continue;
759 }
760
761 pcmk__add_word(&list_text, 1024, host->priv->name);
762 active_instances++;
763 }
764 g_list_free(promoted_list);
765
766 if ((list_text != NULL) && (list_text->len > 0)) {
767 clone_header(out, &rc, rsc, clone_data, desc);
768
769 out->list_item(out, NULL, PCMK_ROLE_PROMOTED ": [ %s ]",
770 (const char *) list_text->str);
771 g_string_truncate(list_text, 0);
772 }
773
774
775 started_list = g_list_sort(started_list, pe__cmp_node_name);
776 for (gIter = started_list; gIter; gIter = gIter->next) {
777 pcmk_node_t *host = gIter->data;
778
779 if (!pcmk__str_in_list(host->priv->name, only_node,
780 pcmk__str_star_matches|pcmk__str_casei)) {
781 continue;
782 }
783
784 pcmk__add_word(&list_text, 1024, host->priv->name);
785 active_instances++;
786 }
787 g_list_free(started_list);
788
789 if ((list_text != NULL) && (list_text->len > 0)) {
790 clone_header(out, &rc, rsc, clone_data, desc);
791
792 if (pcmk_is_set(rsc->flags, pcmk__rsc_promotable)) {
793 enum rsc_role_e role = configured_role(rsc);
794
795 if (role == pcmk_role_unpromoted) {
796 out->list_item(out, NULL,
797 PCMK_ROLE_UNPROMOTED
798 " (" PCMK_META_TARGET_ROLE "): [ %s ]",
799 (const char *) list_text->str);
800 } else {
801 out->list_item(out, NULL, PCMK_ROLE_UNPROMOTED ": [ %s ]",
802 (const char *) list_text->str);
803 }
804
805 } else {
806 out->list_item(out, NULL, "Started: [ %s ]",
807 (const char *) list_text->str);
808 }
809 }
810
811 if (list_text != NULL) {
812 g_string_free(list_text, TRUE);
813 }
814
815 if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
816 if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)
817 && (clone_data->clone_max > active_instances)) {
818
819 GList *nIter;
820 GList *list = g_hash_table_get_values(rsc->priv->allowed_nodes);
821
822
823 if (stopped != NULL) {
824 g_hash_table_destroy(stopped);
825 stopped = NULL;
826 }
827
828 if (list == NULL) {
829
830
831
832
833 list = g_hash_table_get_values(rsc->priv->probed_nodes);
834 }
835
836 list = g_list_sort(list, pe__cmp_node_name);
837 for (nIter = list; nIter != NULL; nIter = nIter->next) {
838 pcmk_node_t *node = (pcmk_node_t *) nIter->data;
839
840 if ((pcmk__find_node_in_list(rsc->priv->active_nodes,
841 node->priv->name) == NULL)
842 && pcmk__str_in_list(node->priv->name, only_node,
843 pcmk__str_star_matches|pcmk__str_casei)) {
844
845 xmlNode *probe_op = NULL;
846 const char *state = "Stopped";
847
848 if (configured_role(rsc) == pcmk_role_stopped) {
849 state = "Stopped (disabled)";
850 }
851
852 if (stopped == NULL) {
853 stopped = pcmk__strkey_table(free, free);
854 }
855
856 probe_op = pe__failed_probe_for_rsc(rsc,
857 node->priv->name);
858 if (probe_op != NULL) {
859 int rc;
860
861 pcmk__scan_min_int(crm_element_value(probe_op,
862 PCMK__XA_RC_CODE),
863 &rc, 0);
864 g_hash_table_insert(stopped, strdup(node->priv->name),
865 crm_strdup_printf("Stopped (%s)",
866 crm_exit_str(rc)));
867 } else {
868 pcmk__insert_dup(stopped, node->priv->name, state);
869 }
870 }
871 }
872 g_list_free(list);
873 }
874
875 if (stopped != NULL) {
876 GList *list = sorted_hash_table_values(stopped);
877
878 clone_header(out, &rc, rsc, clone_data, desc);
879
880 for (GList *status_iter = list; status_iter != NULL; status_iter = status_iter->next) {
881 const char *status = status_iter->data;
882 GList *nodes = nodes_with_status(stopped, status);
883 GString *nodes_str = node_list_to_str(nodes);
884
885 if (nodes_str != NULL) {
886 if (nodes_str->len > 0) {
887 out->list_item(out, NULL, "%s: [ %s ]", status,
888 (const char *) nodes_str->str);
889 }
890 g_string_free(nodes_str, TRUE);
891 }
892
893 g_list_free(nodes);
894 }
895
896 g_list_free(list);
897 g_hash_table_destroy(stopped);
898
899
900
901
902
903 } else if (active_instances == 0) {
904 clone_header(out, &rc, rsc, clone_data, desc);
905 PCMK__OUTPUT_LIST_FOOTER(out, rc);
906 return rc;
907 }
908 }
909
910 PCMK__OUTPUT_LIST_FOOTER(out, rc);
911 return rc;
912 }
913
914 void
915 clone_free(pcmk_resource_t * rsc)
916 {
917 clone_variant_data_t *clone_data = NULL;
918
919 get_clone_variant_data(clone_data, rsc);
920
921 pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
922
923 for (GList *gIter = rsc->priv->children;
924 gIter != NULL; gIter = gIter->next) {
925
926 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
927
928 pcmk__assert(child_rsc != NULL);
929 pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
930 pcmk__xml_free(child_rsc->priv->xml);
931 child_rsc->priv->xml = NULL;
932
933 pcmk__xml_free(child_rsc->priv->orig_xml);
934 child_rsc->priv->orig_xml = NULL;
935 child_rsc->priv->fns->free(child_rsc);
936 }
937
938 g_list_free(rsc->priv->children);
939
940 if (clone_data) {
941 pcmk__assert((clone_data->demote_notify == NULL)
942 && (clone_data->stop_notify == NULL)
943 && (clone_data->start_notify == NULL)
944 && (clone_data->promote_notify == NULL));
945 }
946
947 common_free(rsc);
948 }
949
950 enum rsc_role_e
951 clone_resource_state(const pcmk_resource_t * rsc, gboolean current)
952 {
953 enum rsc_role_e clone_role = pcmk_role_unknown;
954
955 for (GList *gIter = rsc->priv->children;
956 gIter != NULL; gIter = gIter->next) {
957
958 pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
959 enum rsc_role_e a_role = child_rsc->priv->fns->state(child_rsc,
960 current);
961
962 if (a_role > clone_role) {
963 clone_role = a_role;
964 }
965 }
966
967 pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(clone_role));
968 return clone_role;
969 }
970
971
972
973
974
975
976
977
978 bool
979 pe__is_universal_clone(const pcmk_resource_t *rsc,
980 const pcmk_scheduler_t *scheduler)
981 {
982 if (pcmk__is_clone(rsc)) {
983 clone_variant_data_t *clone_data = rsc->priv->variant_opaque;
984
985 if (clone_data->clone_max == g_list_length(scheduler->nodes)) {
986 return TRUE;
987 }
988 }
989 return FALSE;
990 }
991
992 gboolean
993 pe__clone_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
994 gboolean check_parent)
995 {
996 gboolean passes = FALSE;
997 clone_variant_data_t *clone_data = NULL;
998
999 if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
1000 passes = TRUE;
1001 } else {
1002 get_clone_variant_data(clone_data, rsc);
1003 passes = pcmk__str_in_list(pcmk__xe_id(clone_data->xml_obj_child),
1004 only_rsc, pcmk__str_star_matches);
1005
1006 if (!passes) {
1007 for (const GList *iter = rsc->priv->children;
1008 iter != NULL; iter = iter->next) {
1009
1010 const pcmk_resource_t *child_rsc = NULL;
1011
1012 child_rsc = (const pcmk_resource_t *) iter->data;
1013 if (!child_rsc->priv->fns->is_filtered(child_rsc, only_rsc,
1014 FALSE)) {
1015 passes = TRUE;
1016 break;
1017 }
1018 }
1019 }
1020 }
1021 return !passes;
1022 }
1023
1024 const char *
1025 pe__clone_child_id(const pcmk_resource_t *rsc)
1026 {
1027 clone_variant_data_t *clone_data = NULL;
1028 get_clone_variant_data(clone_data, rsc);
1029 return pcmk__xe_id(clone_data->xml_obj_child);
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040 bool
1041 pe__clone_is_ordered(const pcmk_resource_t *clone)
1042 {
1043 clone_variant_data_t *clone_data = NULL;
1044
1045 get_clone_variant_data(clone_data, clone);
1046 return pcmk_is_set(clone_data->flags, pcmk__clone_ordered);
1047 }
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059 int
1060 pe__set_clone_flag(pcmk_resource_t *clone, enum pcmk__clone_flags flag)
1061 {
1062 clone_variant_data_t *clone_data = NULL;
1063
1064 get_clone_variant_data(clone_data, clone);
1065 if (pcmk_is_set(clone_data->flags, flag)) {
1066 return pcmk_rc_already;
1067 }
1068 clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
1069 "Clone", clone->id,
1070 clone_data->flags, flag, "flag");
1071 return pcmk_rc_ok;
1072 }
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083 bool
1084 pe__clone_flag_is_set(const pcmk_resource_t *clone, uint32_t flags)
1085 {
1086 clone_variant_data_t *clone_data = NULL;
1087
1088 get_clone_variant_data(clone_data, clone);
1089 pcmk__assert(clone_data != NULL);
1090
1091 return pcmk_all_flags_set(clone_data->flags, flags);
1092 }
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102 void
1103 pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting,
1104 bool any_demoting)
1105 {
1106 pcmk_action_t *action = NULL;
1107 pcmk_action_t *action_complete = NULL;
1108 clone_variant_data_t *clone_data = NULL;
1109
1110 get_clone_variant_data(clone_data, clone);
1111
1112
1113 action = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTE,
1114 !any_promoting, true);
1115
1116
1117 action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_PROMOTED,
1118 !any_promoting, true);
1119 action_complete->priority = PCMK_SCORE_INFINITY;
1120
1121
1122 if (clone_data->promote_notify == NULL) {
1123 clone_data->promote_notify = pe__action_notif_pseudo_ops(clone,
1124 PCMK_ACTION_PROMOTE,
1125 action,
1126 action_complete);
1127 }
1128
1129
1130 action = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTE,
1131 !any_demoting, true);
1132
1133
1134 action_complete = pe__new_rsc_pseudo_action(clone, PCMK_ACTION_DEMOTED,
1135 !any_demoting, true);
1136 action_complete->priority = PCMK_SCORE_INFINITY;
1137
1138
1139 if (clone_data->demote_notify == NULL) {
1140 clone_data->demote_notify = pe__action_notif_pseudo_ops(clone,
1141 PCMK_ACTION_DEMOTE,
1142 action,
1143 action_complete);
1144
1145 if (clone_data->promote_notify != NULL) {
1146 order_actions(clone_data->stop_notify->post_done,
1147 clone_data->promote_notify->pre, pcmk__ar_ordered);
1148 order_actions(clone_data->start_notify->post_done,
1149 clone_data->promote_notify->pre, pcmk__ar_ordered);
1150 order_actions(clone_data->demote_notify->post_done,
1151 clone_data->promote_notify->pre, pcmk__ar_ordered);
1152 order_actions(clone_data->demote_notify->post_done,
1153 clone_data->start_notify->pre, pcmk__ar_ordered);
1154 order_actions(clone_data->demote_notify->post_done,
1155 clone_data->stop_notify->pre, pcmk__ar_ordered);
1156 }
1157 }
1158 }
1159
1160
1161
1162
1163
1164
1165
1166 void
1167 pe__create_clone_notifications(pcmk_resource_t *clone)
1168 {
1169 clone_variant_data_t *clone_data = NULL;
1170
1171 get_clone_variant_data(clone_data, clone);
1172
1173 pe__create_action_notifications(clone, clone_data->start_notify);
1174 pe__create_action_notifications(clone, clone_data->stop_notify);
1175 pe__create_action_notifications(clone, clone_data->promote_notify);
1176 pe__create_action_notifications(clone, clone_data->demote_notify);
1177 }
1178
1179
1180
1181
1182
1183
1184
1185 void
1186 pe__free_clone_notification_data(pcmk_resource_t *clone)
1187 {
1188 clone_variant_data_t *clone_data = NULL;
1189
1190 get_clone_variant_data(clone_data, clone);
1191
1192 pe__free_action_notification_data(clone_data->demote_notify);
1193 clone_data->demote_notify = NULL;
1194
1195 pe__free_action_notification_data(clone_data->stop_notify);
1196 clone_data->stop_notify = NULL;
1197
1198 pe__free_action_notification_data(clone_data->start_notify);
1199 clone_data->start_notify = NULL;
1200
1201 pe__free_action_notification_data(clone_data->promote_notify);
1202 clone_data->promote_notify = NULL;
1203 }
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215 void
1216 pe__create_clone_notif_pseudo_ops(pcmk_resource_t *clone,
1217 pcmk_action_t *start, pcmk_action_t *started,
1218 pcmk_action_t *stop, pcmk_action_t *stopped)
1219 {
1220 clone_variant_data_t *clone_data = NULL;
1221
1222 get_clone_variant_data(clone_data, clone);
1223
1224 if (clone_data->start_notify == NULL) {
1225 clone_data->start_notify = pe__action_notif_pseudo_ops(clone,
1226 PCMK_ACTION_START,
1227 start, started);
1228 }
1229
1230 if (clone_data->stop_notify == NULL) {
1231 clone_data->stop_notify = pe__action_notif_pseudo_ops(clone,
1232 PCMK_ACTION_STOP,
1233 stop, stopped);
1234 if ((clone_data->start_notify != NULL)
1235 && (clone_data->stop_notify != NULL)) {
1236 order_actions(clone_data->stop_notify->post_done,
1237 clone_data->start_notify->pre, pcmk__ar_ordered);
1238 }
1239 }
1240 }
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250 unsigned int
1251 pe__clone_max_per_node(const pcmk_resource_t *rsc)
1252 {
1253 const clone_variant_data_t *clone_data = NULL;
1254
1255 get_clone_variant_data(clone_data, rsc);
1256 return clone_data->clone_node_max;
1257 }