1 /*
2 * Copyright 2004-2023 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 General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <stdbool.h>
13
14 #include <crm/msg_xml.h>
15 #include <pacemaker-internal.h>
16
17 #include "libpacemaker_private.h"
18
19 struct assign_data {
20 const pcmk_node_t *prefer;
21 bool stop_if_fail;
22 };
23
24 /*!
25 * \internal
26 * \brief Assign a single bundle replica's resources (other than container)
27 *
28 * \param[in,out] replica Replica to assign
29 * \param[in] user_data Preferred node, if any
30 *
31 * \return true (to indicate that any further replicas should be processed)
32 */
33 static bool
34 assign_replica(pe__bundle_replica_t *replica, void *user_data)
35 {
36 pcmk_node_t *container_host = NULL;
37
38 struct assign_data *assign_data = user_data;
39 const pcmk_node_t *prefer = assign_data->prefer;
40 bool stop_if_fail = assign_data->stop_if_fail;
41
42 const pcmk_resource_t *bundle = pe__const_top_resource(replica->container,
43 true);
44
45 if (replica->ip != NULL) {
46 pe_rsc_trace(bundle, "Assigning bundle %s IP %s",
47 bundle->id, replica->ip->id);
48 replica->ip->cmds->assign(replica->ip, prefer, stop_if_fail);
49 }
50
51 container_host = replica->container->allocated_to;
52 if (replica->remote != NULL) {
53 if (pe__is_guest_or_remote_node(container_host)) {
54 /* REMOTE_CONTAINER_HACK: "Nested" connection resources must be on
55 * the same host because Pacemaker Remote only supports a single
56 * active connection.
57 */
58 pcmk__new_colocation("#replica-remote-with-host-remote", NULL,
59 INFINITY, replica->remote,
60 container_host->details->remote_rsc, NULL,
61 NULL, pcmk__coloc_influence);
62 }
63 pe_rsc_trace(bundle, "Assigning bundle %s connection %s",
64 bundle->id, replica->remote->id);
65 replica->remote->cmds->assign(replica->remote, prefer, stop_if_fail);
66 }
67
68 if (replica->child != NULL) {
69 pcmk_node_t *node = NULL;
70 GHashTableIter iter;
71
72 g_hash_table_iter_init(&iter, replica->child->allowed_nodes);
73 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
74 if (!pe__same_node(node, replica->node)) {
75 node->weight = -INFINITY;
76 } else if (!pcmk__threshold_reached(replica->child, node, NULL)) {
77 node->weight = INFINITY;
78 }
79 }
80
81 pe__set_resource_flags(replica->child->parent, pcmk_rsc_assigning);
82 pe_rsc_trace(bundle, "Assigning bundle %s replica child %s",
83 bundle->id, replica->child->id);
84 replica->child->cmds->assign(replica->child, replica->node,
85 stop_if_fail);
86 pe__clear_resource_flags(replica->child->parent, pcmk_rsc_assigning);
87 }
88 return true;
89 }
90
91 /*!
92 * \internal
93 * \brief Assign a bundle resource to a node
94 *
95 * \param[in,out] rsc Resource to assign to a node
96 * \param[in] prefer Node to prefer, if all else is equal
97 * \param[in] stop_if_fail If \c true and a primitive descendant of \p rsc
98 * can't be assigned to a node, set the
99 * descendant's next role to stopped and update
100 * existing actions
101 *
102 * \return Node that \p rsc is assigned to, if assigned entirely to one node
103 *
104 * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
105 * completely undo the assignment. A successful assignment can be either
106 * undone or left alone as final. A failed assignment has the same effect
107 * as calling pcmk__unassign_resource(); there are no side effects on
108 * roles or actions.
109 */
110 pcmk_node_t *
111 pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
112 bool stop_if_fail)
113 {
114 GList *containers = NULL;
115 pcmk_resource_t *bundled_resource = NULL;
116 struct assign_data assign_data = { prefer, stop_if_fail };
117
118 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
119
120 pe_rsc_trace(rsc, "Assigning bundle %s", rsc->id);
121 pe__set_resource_flags(rsc, pcmk_rsc_assigning);
122
123 pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags,
124 pcmk_sched_output_scores),
125 rsc, __func__, rsc->allowed_nodes, rsc->cluster);
126
127 // Assign all containers first, so we know what nodes the bundle will be on
128 containers = g_list_sort(pe__bundle_containers(rsc), pcmk__cmp_instance);
129 pcmk__assign_instances(rsc, containers, pe__bundle_max(rsc),
130 rsc->fns->max_per_node(rsc));
131 g_list_free(containers);
132
133 // Then assign remaining replica resources
134 pe__foreach_bundle_replica(rsc, assign_replica, (void *) &assign_data);
135
136 // Finally, assign the bundled resources to each bundle node
137 bundled_resource = pe__bundled_resource(rsc);
138 if (bundled_resource != NULL) {
139 pcmk_node_t *node = NULL;
140 GHashTableIter iter;
141
142 g_hash_table_iter_init(&iter, bundled_resource->allowed_nodes);
143 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
144 if (pe__node_is_bundle_instance(rsc, node)) {
145 node->weight = 0;
146 } else {
147 node->weight = -INFINITY;
148 }
149 }
150 bundled_resource->cmds->assign(bundled_resource, prefer, stop_if_fail);
151 }
152
153 pe__clear_resource_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned);
154 return NULL;
155 }
156
157 /*!
158 * \internal
159 * \brief Create actions for a bundle replica's resources (other than child)
160 *
161 * \param[in,out] replica Replica to create actions for
162 * \param[in] user_data Unused
163 *
164 * \return true (to indicate that any further replicas should be processed)
165 */
166 static bool
167 create_replica_actions(pe__bundle_replica_t *replica, void *user_data)
168 {
169 if (replica->ip != NULL) {
170 replica->ip->cmds->create_actions(replica->ip);
171 }
172 if (replica->container != NULL) {
173 replica->container->cmds->create_actions(replica->container);
174 }
175 if (replica->remote != NULL) {
176 replica->remote->cmds->create_actions(replica->remote);
177 }
178 return true;
179 }
180
181 /*!
182 * \internal
183 * \brief Create all actions needed for a given bundle resource
184 *
185 * \param[in,out] rsc Bundle resource to create actions for
186 */
187 void
188 pcmk__bundle_create_actions(pcmk_resource_t *rsc)
189 {
190 pcmk_action_t *action = NULL;
191 GList *containers = NULL;
192 pcmk_resource_t *bundled_resource = NULL;
193
194 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
195
196 pe__foreach_bundle_replica(rsc, create_replica_actions, NULL);
197
198 containers = pe__bundle_containers(rsc);
199 pcmk__create_instance_actions(rsc, containers);
200 g_list_free(containers);
201
202 bundled_resource = pe__bundled_resource(rsc);
203 if (bundled_resource != NULL) {
204 bundled_resource->cmds->create_actions(bundled_resource);
205
206 if (pcmk_is_set(bundled_resource->flags, pcmk_rsc_promotable)) {
207 pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTE, true, true);
208 action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTED,
209 true, true);
210 action->priority = INFINITY;
211
212 pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTE, true, true);
213 action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTED,
214 true, true);
215 action->priority = INFINITY;
216 }
217 }
218 }
219
220 /*!
221 * \internal
222 * \brief Create internal constraints for a bundle replica's resources
223 *
224 * \param[in,out] replica Replica to create internal constraints for
225 * \param[in,out] user_data Replica's parent bundle
226 *
227 * \return true (to indicate that any further replicas should be processed)
228 */
229 static bool
230 replica_internal_constraints(pe__bundle_replica_t *replica, void *user_data)
231 {
232 pcmk_resource_t *bundle = user_data;
233
234 replica->container->cmds->internal_constraints(replica->container);
235
236 // Start bundle -> start replica container
237 pcmk__order_starts(bundle, replica->container,
238 pcmk__ar_unrunnable_first_blocks
239 |pcmk__ar_then_implies_first_graphed);
240
241 // Stop bundle -> stop replica child and container
242 if (replica->child != NULL) {
243 pcmk__order_stops(bundle, replica->child,
244 pcmk__ar_then_implies_first_graphed);
245 }
246 pcmk__order_stops(bundle, replica->container,
247 pcmk__ar_then_implies_first_graphed);
248
249 // Start replica container -> bundle is started
250 pcmk__order_resource_actions(replica->container, PCMK_ACTION_START, bundle,
251 PCMK_ACTION_RUNNING,
252 pcmk__ar_first_implies_then_graphed);
253
254 // Stop replica container -> bundle is stopped
255 pcmk__order_resource_actions(replica->container, PCMK_ACTION_STOP, bundle,
256 PCMK_ACTION_STOPPED,
257 pcmk__ar_first_implies_then_graphed);
258
259 if (replica->ip != NULL) {
260 replica->ip->cmds->internal_constraints(replica->ip);
261
262 // Replica IP address -> replica container (symmetric)
263 pcmk__order_starts(replica->ip, replica->container,
264 pcmk__ar_unrunnable_first_blocks
265 |pcmk__ar_guest_allowed);
266 pcmk__order_stops(replica->container, replica->ip,
267 pcmk__ar_then_implies_first|pcmk__ar_guest_allowed);
268
269 pcmk__new_colocation("#ip-with-container", NULL, INFINITY, replica->ip,
270 replica->container, NULL, NULL,
271 pcmk__coloc_influence);
272 }
273
274 if (replica->remote != NULL) {
275 /* This handles ordering and colocating remote relative to container
276 * (via "#resource-with-container"). Since IP is also ordered and
277 * colocated relative to the container, we don't need to do anything
278 * explicit here with IP.
279 */
280 replica->remote->cmds->internal_constraints(replica->remote);
281 }
282
283 if (replica->child != NULL) {
284 CRM_ASSERT(replica->remote != NULL);
285 // "Start remote then child" is implicit in scheduler's remote logic
286 }
287 return true;
288 }
289
290 /*!
291 * \internal
292 * \brief Create implicit constraints needed for a bundle resource
293 *
294 * \param[in,out] rsc Bundle resource to create implicit constraints for
295 */
296 void
297 pcmk__bundle_internal_constraints(pcmk_resource_t *rsc)
298 {
299 pcmk_resource_t *bundled_resource = NULL;
300
301 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
302
303 pe__foreach_bundle_replica(rsc, replica_internal_constraints, rsc);
304
305 bundled_resource = pe__bundled_resource(rsc);
306 if (bundled_resource == NULL) {
307 return;
308 }
309
310 // Start bundle -> start bundled clone
311 pcmk__order_resource_actions(rsc, PCMK_ACTION_START, bundled_resource,
312 PCMK_ACTION_START,
313 pcmk__ar_then_implies_first_graphed);
314
315 // Bundled clone is started -> bundle is started
316 pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_RUNNING,
317 rsc, PCMK_ACTION_RUNNING,
318 pcmk__ar_first_implies_then_graphed);
319
320 // Stop bundle -> stop bundled clone
321 pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP, bundled_resource,
322 PCMK_ACTION_STOP,
323 pcmk__ar_then_implies_first_graphed);
324
325 // Bundled clone is stopped -> bundle is stopped
326 pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_STOPPED,
327 rsc, PCMK_ACTION_STOPPED,
328 pcmk__ar_first_implies_then_graphed);
329
330 bundled_resource->cmds->internal_constraints(bundled_resource);
331
332 if (!pcmk_is_set(bundled_resource->flags, pcmk_rsc_promotable)) {
333 return;
334 }
335 pcmk__promotable_restart_ordering(rsc);
336
337 // Demote bundle -> demote bundled clone
338 pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTE, bundled_resource,
339 PCMK_ACTION_DEMOTE,
340 pcmk__ar_then_implies_first_graphed);
341
342 // Bundled clone is demoted -> bundle is demoted
343 pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_DEMOTED,
344 rsc, PCMK_ACTION_DEMOTED,
345 pcmk__ar_first_implies_then_graphed);
346
347 // Promote bundle -> promote bundled clone
348 pcmk__order_resource_actions(rsc, PCMK_ACTION_PROMOTE,
349 bundled_resource, PCMK_ACTION_PROMOTE,
350 pcmk__ar_then_implies_first_graphed);
351
352 // Bundled clone is promoted -> bundle is promoted
353 pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_PROMOTED,
354 rsc, PCMK_ACTION_PROMOTED,
355 pcmk__ar_first_implies_then_graphed);
356 }
357
358 struct match_data {
359 const pcmk_node_t *node; // Node to compare against replica
360 pcmk_resource_t *container; // Replica container corresponding to node
361 };
362
363 /*!
364 * \internal
365 * \brief Check whether a replica container is assigned to a given node
366 *
367 * \param[in] replica Replica to check
368 * \param[in,out] user_data struct match_data with node to compare against
369 *
370 * \return true if the replica does not match (to indicate further replicas
371 * should be processed), otherwise false
372 */
373 static bool
374 match_replica_container(const pe__bundle_replica_t *replica, void *user_data)
375 {
376 struct match_data *match_data = user_data;
377
378 if (pcmk__instance_matches(replica->container, match_data->node,
379 pcmk_role_unknown, false)) {
380 match_data->container = replica->container;
381 return false; // Match found, don't bother searching further replicas
382 }
383 return true; // No match, keep searching
384 }
385
386 /*!
387 * \internal
388 * \brief Get the host to which a bundle node is assigned
389 *
390 * \param[in] node Possible bundle node to check
391 *
392 * \return Node to which the container for \p node is assigned if \p node is a
393 * bundle node, otherwise \p node itself
394 */
395 static const pcmk_node_t *
396 get_bundle_node_host(const pcmk_node_t *node)
397 {
398 if (pe__is_bundle_node(node)) {
399 const pcmk_resource_t *container = node->details->remote_rsc->container;
400
401 return container->fns->location(container, NULL, 0);
402 }
403 return node;
404 }
405
406 /*!
407 * \internal
408 * \brief Find a bundle container compatible with a dependent resource
409 *
410 * \param[in] dependent Dependent resource in colocation with bundle
411 * \param[in] bundle Bundle that \p dependent is colocated with
412 *
413 * \return A container from \p bundle assigned to the same node as \p dependent
414 * if assigned, otherwise assigned to any of dependent's allowed nodes,
415 * otherwise NULL.
416 */
417 static pcmk_resource_t *
418 compatible_container(const pcmk_resource_t *dependent,
419 const pcmk_resource_t *bundle)
420 {
421 GList *scratch = NULL;
422 struct match_data match_data = { NULL, NULL };
423
424 // If dependent is assigned, only check there
425 match_data.node = dependent->fns->location(dependent, NULL, 0);
426 match_data.node = get_bundle_node_host(match_data.node);
427 if (match_data.node != NULL) {
428 pe__foreach_const_bundle_replica(bundle, match_replica_container,
429 &match_data);
430 return match_data.container;
431 }
432
433 // Otherwise, check for any of the dependent's allowed nodes
434 scratch = g_hash_table_get_values(dependent->allowed_nodes);
435 scratch = pcmk__sort_nodes(scratch, NULL);
436 for (const GList *iter = scratch; iter != NULL; iter = iter->next) {
437 match_data.node = iter->data;
438 match_data.node = get_bundle_node_host(match_data.node);
439 if (match_data.node == NULL) {
440 continue;
441 }
442
443 pe__foreach_const_bundle_replica(bundle, match_replica_container,
444 &match_data);
445 if (match_data.container != NULL) {
446 break;
447 }
448 }
449 g_list_free(scratch);
450 return match_data.container;
451 }
452
453 struct coloc_data {
454 const pcmk__colocation_t *colocation;
455 pcmk_resource_t *dependent;
456 GList *container_hosts;
457 };
458
459 /*!
460 * \internal
461 * \brief Apply a colocation score to replica node scores or resource priority
462 *
463 * \param[in] replica Replica of primary bundle resource in colocation
464 * \param[in,out] user_data struct coloc_data for colocation being applied
465 *
466 * \return true (to indicate that any further replicas should be processed)
467 */
468 static bool
469 replica_apply_coloc_score(const pe__bundle_replica_t *replica, void *user_data)
470 {
471 struct coloc_data *coloc_data = user_data;
472 pcmk_node_t *chosen = NULL;
473
474 if (coloc_data->colocation->score < INFINITY) {
475 replica->container->cmds->apply_coloc_score(coloc_data->dependent,
476 replica->container,
477 coloc_data->colocation,
478 false);
479 return true;
480 }
481
482 chosen = replica->container->fns->location(replica->container, NULL, 0);
483 if ((chosen == NULL)
484 || is_set_recursive(replica->container, pcmk_rsc_blocked, true)) {
485 return true;
486 }
487
488 if ((coloc_data->colocation->primary_role >= pcmk_role_promoted)
489 && ((replica->child == NULL)
490 || (replica->child->next_role < pcmk_role_promoted))) {
491 return true;
492 }
493
494 pe_rsc_trace(pe__const_top_resource(replica->container, true),
495 "Allowing mandatory colocation %s using %s @%d",
496 coloc_data->colocation->id, pe__node_name(chosen),
497 chosen->weight);
498 coloc_data->container_hosts = g_list_prepend(coloc_data->container_hosts,
499 chosen);
500 return true;
501 }
502
503 /*!
504 * \internal
505 * \brief Apply a colocation's score to node scores or resource priority
506 *
507 * Given a colocation constraint, apply its score to the dependent's
508 * allowed node scores (if we are still placing resources) or priority (if
509 * we are choosing promotable clone instance roles).
510 *
511 * \param[in,out] dependent Dependent resource in colocation
512 * \param[in] primary Primary resource in colocation
513 * \param[in] colocation Colocation constraint to apply
514 * \param[in] for_dependent true if called on behalf of dependent
515 */
516 void
517 pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent,
518 const pcmk_resource_t *primary,
519 const pcmk__colocation_t *colocation,
520 bool for_dependent)
521 {
522 struct coloc_data coloc_data = { colocation, dependent, NULL };
523
524 /* This should never be called for the bundle itself as a dependent.
525 * Instead, we add its colocation constraints to its containers and bundled
526 * primitive and call the apply_coloc_score() method for them as dependents.
527 */
528 CRM_ASSERT((primary != NULL)
529 && (primary->variant == pcmk_rsc_variant_bundle)
530 && (dependent != NULL)
531 && (dependent->variant == pcmk_rsc_variant_primitive)
532 && (colocation != NULL) && !for_dependent);
533
534 if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
535 pe_rsc_trace(primary,
536 "Skipping applying colocation %s "
537 "because %s is still provisional",
538 colocation->id, primary->id);
539 return;
540 }
541 pe_rsc_trace(primary, "Applying colocation %s (%s with %s at %s)",
542 colocation->id, dependent->id, primary->id,
543 pcmk_readable_score(colocation->score));
544
545 /* If the constraint dependent is a clone or bundle, "dependent" here is one
546 * of its instances. Look for a compatible instance of this bundle.
547 */
548 if (colocation->dependent->variant > pcmk_rsc_variant_group) {
549 const pcmk_resource_t *primary_container = NULL;
550
551 primary_container = compatible_container(dependent, primary);
552 if (primary_container != NULL) { // Success, we found one
553 pe_rsc_debug(primary, "Pairing %s with %s",
554 dependent->id, primary_container->id);
555 dependent->cmds->apply_coloc_score(dependent, primary_container,
556 colocation, true);
557
558 } else if (colocation->score >= INFINITY) { // Failure, and it's fatal
559 crm_notice("%s cannot run because there is no compatible "
560 "instance of %s to colocate with",
561 dependent->id, primary->id);
562 pcmk__assign_resource(dependent, NULL, true, true);
563
564 } else { // Failure, but we can ignore it
565 pe_rsc_debug(primary,
566 "%s cannot be colocated with any instance of %s",
567 dependent->id, primary->id);
568 }
569 return;
570 }
571
572 pe__foreach_const_bundle_replica(primary, replica_apply_coloc_score,
573 &coloc_data);
574
575 if (colocation->score >= INFINITY) {
576 pcmk__colocation_intersect_nodes(dependent, primary, colocation,
577 coloc_data.container_hosts, false);
578 }
579 g_list_free(coloc_data.container_hosts);
580 }
581
582 // Bundle implementation of pcmk_assignment_methods_t:with_this_colocations()
583 void
584 pcmk__with_bundle_colocations(const pcmk_resource_t *rsc,
585 const pcmk_resource_t *orig_rsc, GList **list)
586 {
587 const pcmk_resource_t *bundled_rsc = NULL;
588
589 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
590 && (orig_rsc != NULL) && (list != NULL));
591
592 // The bundle itself and its containers always get its colocations
593 if ((orig_rsc == rsc)
594 || pcmk_is_set(orig_rsc->flags, pcmk_rsc_replica_container)) {
595
596 pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
597 return;
598 }
599
600 /* The bundled resource gets the colocations if it's promotable and we've
601 * begun choosing roles
602 */
603 bundled_rsc = pe__bundled_resource(rsc);
604 if ((bundled_rsc == NULL)
605 || !pcmk_is_set(bundled_rsc->flags, pcmk_rsc_promotable)
606 || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) {
607 return;
608 }
609
610 if (orig_rsc == bundled_rsc) {
611 if (pe__clone_flag_is_set(orig_rsc,
612 pcmk__clone_promotion_constrained)) {
613 /* orig_rsc is the clone and we're setting roles (or have already
614 * done so)
615 */
616 pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
617 }
618
619 } else if (!pcmk_is_set(orig_rsc->flags, pcmk_rsc_unassigned)) {
620 /* orig_rsc is an instance and is already assigned. If something
621 * requests colocations for orig_rsc now, it's for setting roles.
622 */
623 pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
624 }
625 }
626
627 // Bundle implementation of pcmk_assignment_methods_t:this_with_colocations()
628 void
629 pcmk__bundle_with_colocations(const pcmk_resource_t *rsc,
630 const pcmk_resource_t *orig_rsc, GList **list)
631 {
632 const pcmk_resource_t *bundled_rsc = NULL;
633
634 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
635 && (orig_rsc != NULL) && (list != NULL));
636
637 // The bundle itself and its containers always get its colocations
638 if ((orig_rsc == rsc)
639 || pcmk_is_set(orig_rsc->flags, pcmk_rsc_replica_container)) {
640
641 pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
642 return;
643 }
644
645 /* The bundled resource gets the colocations if it's promotable and we've
646 * begun choosing roles
647 */
648 bundled_rsc = pe__bundled_resource(rsc);
649 if ((bundled_rsc == NULL)
650 || !pcmk_is_set(bundled_rsc->flags, pcmk_rsc_promotable)
651 || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) {
652 return;
653 }
654
655 if (orig_rsc == bundled_rsc) {
656 if (pe__clone_flag_is_set(orig_rsc,
657 pcmk__clone_promotion_constrained)) {
658 /* orig_rsc is the clone and we're setting roles (or have already
659 * done so)
660 */
661 pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
662 }
663
664 } else if (!pcmk_is_set(orig_rsc->flags, pcmk_rsc_unassigned)) {
665 /* orig_rsc is an instance and is already assigned. If something
666 * requests colocations for orig_rsc now, it's for setting roles.
667 */
668 pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
669 }
670 }
671
672 /*!
673 * \internal
674 * \brief Return action flags for a given bundle resource action
675 *
676 * \param[in,out] action Bundle resource action to get flags for
677 * \param[in] node If not NULL, limit effects to this node
678 *
679 * \return Flags appropriate to \p action on \p node
680 */
681 uint32_t
682 pcmk__bundle_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
683 {
684 GList *containers = NULL;
685 uint32_t flags = 0;
686 pcmk_resource_t *bundled_resource = NULL;
687
688 CRM_ASSERT((action != NULL) && (action->rsc != NULL)
689 && (action->rsc->variant == pcmk_rsc_variant_bundle));
690
691 bundled_resource = pe__bundled_resource(action->rsc);
692 if (bundled_resource != NULL) {
693 // Clone actions are done on the bundled clone resource, not container
694 switch (get_complex_task(bundled_resource, action->task)) {
695 case pcmk_action_unspecified:
696 case pcmk_action_notify:
697 case pcmk_action_notified:
698 case pcmk_action_promote:
699 case pcmk_action_promoted:
700 case pcmk_action_demote:
701 case pcmk_action_demoted:
702 return pcmk__collective_action_flags(action,
703 bundled_resource->children,
704 node);
705 default:
706 break;
707 }
708 }
709
710 containers = pe__bundle_containers(action->rsc);
711 flags = pcmk__collective_action_flags(action, containers, node);
712 g_list_free(containers);
713 return flags;
714 }
715
716 /*!
717 * \internal
718 * \brief Apply a location constraint to a bundle replica
719 *
720 * \param[in,out] replica Replica to apply constraint to
721 * \param[in,out] user_data Location constraint to apply
722 *
723 * \return true (to indicate that any further replicas should be processed)
724 */
725 static bool
726 apply_location_to_replica(pe__bundle_replica_t *replica, void *user_data)
727 {
728 pe__location_t *location = user_data;
729
730 if (replica->container != NULL) {
731 replica->container->cmds->apply_location(replica->container, location);
732 }
733 if (replica->ip != NULL) {
734 replica->ip->cmds->apply_location(replica->ip, location);
735 }
736 return true;
737 }
738
739 /*!
740 * \internal
741 * \brief Apply a location constraint to a bundle resource's allowed node scores
742 *
743 * \param[in,out] rsc Bundle resource to apply constraint to
744 * \param[in,out] location Location constraint to apply
745 */
746 void
747 pcmk__bundle_apply_location(pcmk_resource_t *rsc, pe__location_t *location)
748 {
749 pcmk_resource_t *bundled_resource = NULL;
750
751 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
752 && (location != NULL));
753
754 pcmk__apply_location(rsc, location);
755 pe__foreach_bundle_replica(rsc, apply_location_to_replica, location);
756
757 bundled_resource = pe__bundled_resource(rsc);
758 if ((bundled_resource != NULL)
759 && ((location->role_filter == pcmk_role_unpromoted)
760 || (location->role_filter == pcmk_role_promoted))) {
761 bundled_resource->cmds->apply_location(bundled_resource, location);
762 bundled_resource->rsc_location = g_list_prepend(
763 bundled_resource->rsc_location, location);
764 }
765 }
766
767 #define XPATH_REMOTE "//nvpair[@name='" XML_RSC_ATTR_REMOTE_RA_ADDR "']"
768
769 /*!
770 * \internal
771 * \brief Add a bundle replica's actions to transition graph
772 *
773 * \param[in,out] replica Replica to add to graph
774 * \param[in] user_data Bundle that replica belongs to (for logging only)
775 *
776 * \return true (to indicate that any further replicas should be processed)
777 */
778 static bool
779 add_replica_actions_to_graph(pe__bundle_replica_t *replica, void *user_data)
780 {
781 if ((replica->remote != NULL) && (replica->container != NULL)
782 && pe__bundle_needs_remote_name(replica->remote)) {
783
784 /* REMOTE_CONTAINER_HACK: Allow remote nodes to run containers that
785 * run pacemaker-remoted inside, without needing a separate IP for
786 * the container. This is done by configuring the inner remote's
787 * connection host as the magic string "#uname", then
788 * replacing it with the underlying host when needed.
789 */
790 xmlNode *nvpair = get_xpath_object(XPATH_REMOTE, replica->remote->xml,
791 LOG_ERR);
792 const char *calculated_addr = NULL;
793
794 // Replace the value in replica->remote->xml (if appropriate)
795 calculated_addr = pe__add_bundle_remote_name(replica->remote,
796 replica->remote->cluster,
797 nvpair, "value");
798 if (calculated_addr != NULL) {
799 /* Since this is for the bundle as a resource, and not any
800 * particular action, replace the value in the default
801 * parameters (not evaluated for node). create_graph_action()
802 * will grab it from there to replace it in node-evaluated
803 * parameters.
804 */
805 GHashTable *params = pe_rsc_params(replica->remote,
806 NULL, replica->remote->cluster);
807
808 g_hash_table_replace(params,
809 strdup(XML_RSC_ATTR_REMOTE_RA_ADDR),
810 strdup(calculated_addr));
811 } else {
812 pcmk_resource_t *bundle = user_data;
813
814 /* The only way to get here is if the remote connection is
815 * neither currently running nor scheduled to run. That means we
816 * won't be doing any operations that require addr (only start
817 * requires it; we additionally use it to compare digests when
818 * unpacking status, promote, and migrate_from history, but
819 * that's already happened by this point).
820 */
821 pe_rsc_info(bundle,
822 "Unable to determine address for bundle %s "
823 "remote connection", bundle->id);
824 }
825 }
826 if (replica->ip != NULL) {
827 replica->ip->cmds->add_actions_to_graph(replica->ip);
828 }
829 if (replica->container != NULL) {
830 replica->container->cmds->add_actions_to_graph(replica->container);
831 }
832 if (replica->remote != NULL) {
833 replica->remote->cmds->add_actions_to_graph(replica->remote);
834 }
835 return true;
836 }
837
838 /*!
839 * \internal
840 * \brief Add a bundle resource's actions to the transition graph
841 *
842 * \param[in,out] rsc Bundle resource whose actions should be added
843 */
844 void
845 pcmk__bundle_add_actions_to_graph(pcmk_resource_t *rsc)
846 {
847 pcmk_resource_t *bundled_resource = NULL;
848
849 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
850
851 bundled_resource = pe__bundled_resource(rsc);
852 if (bundled_resource != NULL) {
853 bundled_resource->cmds->add_actions_to_graph(bundled_resource);
854 }
855 pe__foreach_bundle_replica(rsc, add_replica_actions_to_graph, rsc);
856 }
857
858 struct probe_data {
859 pcmk_resource_t *bundle; // Bundle being probed
860 pcmk_node_t *node; // Node to create probes on
861 bool any_created; // Whether any probes have been created
862 };
863
864 /*!
865 * \internal
866 * \brief Order a bundle replica's start after another replica's probe
867 *
868 * \param[in,out] replica Replica to order start for
869 * \param[in,out] user_data Replica with probe to order after
870 *
871 * \return true (to indicate that any further replicas should be processed)
872 */
873 static bool
874 order_replica_start_after(pe__bundle_replica_t *replica, void *user_data)
875 {
876 pe__bundle_replica_t *probed_replica = user_data;
877
878 if ((replica == probed_replica) || (replica->container == NULL)) {
879 return true;
880 }
881 pcmk__new_ordering(probed_replica->container,
882 pcmk__op_key(probed_replica->container->id,
883 PCMK_ACTION_MONITOR, 0),
884 NULL, replica->container,
885 pcmk__op_key(replica->container->id, PCMK_ACTION_START,
886 0),
887 NULL, pcmk__ar_ordered|pcmk__ar_if_on_same_node,
888 replica->container->cluster);
889 return true;
890 }
891
892 /*!
893 * \internal
894 * \brief Create probes for a bundle replica's resources
895 *
896 * \param[in,out] replica Replica to create probes for
897 * \param[in,out] user_data struct probe_data
898 *
899 * \return true (to indicate that any further replicas should be processed)
900 */
901 static bool
902 create_replica_probes(pe__bundle_replica_t *replica, void *user_data)
903 {
904 struct probe_data *probe_data = user_data;
905
906 if ((replica->ip != NULL)
907 && replica->ip->cmds->create_probe(replica->ip, probe_data->node)) {
908 probe_data->any_created = true;
909 }
910 if ((replica->child != NULL)
911 && pe__same_node(probe_data->node, replica->node)
912 && replica->child->cmds->create_probe(replica->child,
913 probe_data->node)) {
914 probe_data->any_created = true;
915 }
916 if ((replica->container != NULL)
917 && replica->container->cmds->create_probe(replica->container,
918 probe_data->node)) {
919 probe_data->any_created = true;
920
921 /* If we're limited to one replica per host (due to
922 * the lack of an IP range probably), then we don't
923 * want any of our peer containers starting until
924 * we've established that no other copies are already
925 * running.
926 *
927 * Partly this is to ensure that the maximum replicas per host is
928 * observed, but also to ensure that the containers
929 * don't fail to start because the necessary port
930 * mappings (which won't include an IP for uniqueness)
931 * are already taken
932 */
933 if (probe_data->bundle->fns->max_per_node(probe_data->bundle) == 1) {
934 pe__foreach_bundle_replica(probe_data->bundle,
935 order_replica_start_after, replica);
936 }
937 }
938 if ((replica->container != NULL) && (replica->remote != NULL)
939 && replica->remote->cmds->create_probe(replica->remote,
940 probe_data->node)) {
941 /* Do not probe the remote resource until we know where the container is
942 * running. This is required for REMOTE_CONTAINER_HACK to correctly
943 * probe remote resources.
944 */
945 char *probe_uuid = pcmk__op_key(replica->remote->id,
946 PCMK_ACTION_MONITOR, 0);
947 pcmk_action_t *probe = find_first_action(replica->remote->actions,
948 probe_uuid, NULL,
949 probe_data->node);
950
951 free(probe_uuid);
952 if (probe != NULL) {
953 probe_data->any_created = true;
954 pe_rsc_trace(probe_data->bundle, "Ordering %s probe on %s",
955 replica->remote->id, pe__node_name(probe_data->node));
956 pcmk__new_ordering(replica->container,
957 pcmk__op_key(replica->container->id,
958 PCMK_ACTION_START, 0),
959 NULL, replica->remote, NULL, probe,
960 pcmk__ar_nested_remote_probe,
961 probe_data->bundle->cluster);
962 }
963 }
964 return true;
965 }
966
967 /*!
968 * \internal
969 *
970 * \brief Schedule any probes needed for a bundle resource on a node
971 *
972 * \param[in,out] rsc Bundle resource to create probes for
973 * \param[in,out] node Node to create probe on
974 *
975 * \return true if any probe was created, otherwise false
976 */
977 bool
978 pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node)
979 {
980 struct probe_data probe_data = { rsc, node, false };
981
982 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
983 pe__foreach_bundle_replica(rsc, create_replica_probes, &probe_data);
984 return probe_data.any_created;
985 }
986
987 /*!
988 * \internal
989 * \brief Output actions for one bundle replica
990 *
991 * \param[in,out] replica Replica to output actions for
992 * \param[in] user_data Unused
993 *
994 * \return true (to indicate that any further replicas should be processed)
995 */
996 static bool
997 output_replica_actions(pe__bundle_replica_t *replica, void *user_data)
998 {
999 if (replica->ip != NULL) {
1000 replica->ip->cmds->output_actions(replica->ip);
1001 }
1002 if (replica->container != NULL) {
1003 replica->container->cmds->output_actions(replica->container);
1004 }
1005 if (replica->remote != NULL) {
1006 replica->remote->cmds->output_actions(replica->remote);
1007 }
1008 if (replica->child != NULL) {
1009 replica->child->cmds->output_actions(replica->child);
1010 }
1011 return true;
1012 }
1013
1014 /*!
1015 * \internal
1016 * \brief Output a summary of scheduled actions for a bundle resource
1017 *
1018 * \param[in,out] rsc Bundle resource to output actions for
1019 */
1020 void
1021 pcmk__output_bundle_actions(pcmk_resource_t *rsc)
1022 {
1023 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
1024 pe__foreach_bundle_replica(rsc, output_replica_actions, NULL);
1025 }
1026
1027 // Bundle implementation of pcmk_assignment_methods_t:add_utilization()
1028 void
1029 pcmk__bundle_add_utilization(const pcmk_resource_t *rsc,
1030 const pcmk_resource_t *orig_rsc, GList *all_rscs,
1031 GHashTable *utilization)
1032 {
1033 pcmk_resource_t *container = NULL;
1034
1035 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
1036
1037 if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
1038 return;
1039 }
1040
1041 /* All bundle replicas are identical, so using the utilization of the first
1042 * is sufficient for any. Only the implicit container resource can have
1043 * utilization values.
1044 */
1045 container = pe__first_container(rsc);
1046 if (container != NULL) {
1047 container->cmds->add_utilization(container, orig_rsc, all_rscs,
1048 utilization);
1049 }
1050 }
1051
1052 // Bundle implementation of pcmk_assignment_methods_t:shutdown_lock()
1053 void
1054 pcmk__bundle_shutdown_lock(pcmk_resource_t *rsc)
1055 {
1056 CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
1057 // Bundles currently don't support shutdown locks
1058 }
1059