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