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 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/common/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 0 : assign_replica(pcmk__bundle_replica_t *replica, void *user_data)
35 : {
36 0 : pcmk_node_t *container_host = NULL;
37 :
38 0 : struct assign_data *assign_data = user_data;
39 0 : const pcmk_node_t *prefer = assign_data->prefer;
40 0 : bool stop_if_fail = assign_data->stop_if_fail;
41 :
42 0 : const pcmk_resource_t *bundle = pe__const_top_resource(replica->container,
43 : true);
44 :
45 0 : if (replica->ip != NULL) {
46 0 : pcmk__rsc_trace(bundle, "Assigning bundle %s IP %s",
47 : bundle->id, replica->ip->id);
48 0 : replica->ip->cmds->assign(replica->ip, prefer, stop_if_fail);
49 : }
50 :
51 0 : container_host = replica->container->allocated_to;
52 0 : if (replica->remote != NULL) {
53 0 : if (pcmk__is_pacemaker_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 0 : pcmk__new_colocation("#replica-remote-with-host-remote", NULL,
59 : PCMK_SCORE_INFINITY, replica->remote,
60 0 : container_host->details->remote_rsc, NULL,
61 : NULL, pcmk__coloc_influence);
62 : }
63 0 : pcmk__rsc_trace(bundle, "Assigning bundle %s connection %s",
64 : bundle->id, replica->remote->id);
65 0 : replica->remote->cmds->assign(replica->remote, prefer, stop_if_fail);
66 : }
67 :
68 0 : if (replica->child != NULL) {
69 0 : pcmk_node_t *node = NULL;
70 : GHashTableIter iter;
71 :
72 0 : g_hash_table_iter_init(&iter, replica->child->allowed_nodes);
73 0 : while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
74 0 : if (!pcmk__same_node(node, replica->node)) {
75 0 : node->weight = -PCMK_SCORE_INFINITY;
76 0 : } else if (!pcmk__threshold_reached(replica->child, node, NULL)) {
77 0 : node->weight = PCMK_SCORE_INFINITY;
78 : }
79 : }
80 :
81 0 : pcmk__set_rsc_flags(replica->child->parent, pcmk_rsc_assigning);
82 0 : pcmk__rsc_trace(bundle, "Assigning bundle %s replica child %s",
83 : bundle->id, replica->child->id);
84 0 : replica->child->cmds->assign(replica->child, replica->node,
85 : stop_if_fail);
86 0 : pcmk__clear_rsc_flags(replica->child->parent, pcmk_rsc_assigning);
87 : }
88 0 : 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 0 : pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
112 : bool stop_if_fail)
113 : {
114 0 : GList *containers = NULL;
115 0 : pcmk_resource_t *bundled_resource = NULL;
116 0 : struct assign_data assign_data = { prefer, stop_if_fail };
117 :
118 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
119 :
120 0 : pcmk__rsc_trace(rsc, "Assigning bundle %s", rsc->id);
121 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning);
122 :
123 0 : 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 0 : containers = g_list_sort(pe__bundle_containers(rsc), pcmk__cmp_instance);
129 0 : pcmk__assign_instances(rsc, containers, pe__bundle_max(rsc),
130 0 : rsc->fns->max_per_node(rsc));
131 0 : g_list_free(containers);
132 :
133 : // Then assign remaining replica resources
134 0 : pe__foreach_bundle_replica(rsc, assign_replica, (void *) &assign_data);
135 :
136 : // Finally, assign the bundled resources to each bundle node
137 0 : bundled_resource = pe__bundled_resource(rsc);
138 0 : if (bundled_resource != NULL) {
139 0 : pcmk_node_t *node = NULL;
140 : GHashTableIter iter;
141 :
142 0 : g_hash_table_iter_init(&iter, bundled_resource->allowed_nodes);
143 0 : while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
144 0 : if (pe__node_is_bundle_instance(rsc, node)) {
145 0 : node->weight = 0;
146 : } else {
147 0 : node->weight = -PCMK_SCORE_INFINITY;
148 : }
149 : }
150 0 : bundled_resource->cmds->assign(bundled_resource, prefer, stop_if_fail);
151 : }
152 :
153 0 : pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned);
154 0 : 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 0 : create_replica_actions(pcmk__bundle_replica_t *replica, void *user_data)
168 : {
169 0 : if (replica->ip != NULL) {
170 0 : replica->ip->cmds->create_actions(replica->ip);
171 : }
172 0 : if (replica->container != NULL) {
173 0 : replica->container->cmds->create_actions(replica->container);
174 : }
175 0 : if (replica->remote != NULL) {
176 0 : replica->remote->cmds->create_actions(replica->remote);
177 : }
178 0 : 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 0 : pcmk__bundle_create_actions(pcmk_resource_t *rsc)
189 : {
190 0 : pcmk_action_t *action = NULL;
191 0 : GList *containers = NULL;
192 0 : pcmk_resource_t *bundled_resource = NULL;
193 :
194 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
195 :
196 0 : pe__foreach_bundle_replica(rsc, create_replica_actions, NULL);
197 :
198 0 : containers = pe__bundle_containers(rsc);
199 0 : pcmk__create_instance_actions(rsc, containers);
200 0 : g_list_free(containers);
201 :
202 0 : bundled_resource = pe__bundled_resource(rsc);
203 0 : if (bundled_resource != NULL) {
204 0 : bundled_resource->cmds->create_actions(bundled_resource);
205 :
206 0 : if (pcmk_is_set(bundled_resource->flags, pcmk_rsc_promotable)) {
207 0 : pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTE, true, true);
208 0 : action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTED,
209 : true, true);
210 0 : action->priority = PCMK_SCORE_INFINITY;
211 :
212 0 : pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTE, true, true);
213 0 : action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTED,
214 : true, true);
215 0 : action->priority = PCMK_SCORE_INFINITY;
216 : }
217 : }
218 0 : }
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 0 : replica_internal_constraints(pcmk__bundle_replica_t *replica, void *user_data)
231 : {
232 0 : pcmk_resource_t *bundle = user_data;
233 :
234 0 : replica->container->cmds->internal_constraints(replica->container);
235 :
236 : // Start bundle -> start replica container
237 0 : 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 0 : if (replica->child != NULL) {
243 0 : pcmk__order_stops(bundle, replica->child,
244 : pcmk__ar_then_implies_first_graphed);
245 : }
246 0 : pcmk__order_stops(bundle, replica->container,
247 : pcmk__ar_then_implies_first_graphed);
248 :
249 : // Start replica container -> bundle is started
250 0 : 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 0 : pcmk__order_resource_actions(replica->container, PCMK_ACTION_STOP, bundle,
256 : PCMK_ACTION_STOPPED,
257 : pcmk__ar_first_implies_then_graphed);
258 :
259 0 : if (replica->ip != NULL) {
260 0 : replica->ip->cmds->internal_constraints(replica->ip);
261 :
262 : // Replica IP address -> replica container (symmetric)
263 0 : pcmk__order_starts(replica->ip, replica->container,
264 : pcmk__ar_unrunnable_first_blocks
265 : |pcmk__ar_guest_allowed);
266 0 : pcmk__order_stops(replica->container, replica->ip,
267 : pcmk__ar_then_implies_first|pcmk__ar_guest_allowed);
268 :
269 0 : pcmk__new_colocation("#ip-with-container", NULL, PCMK_SCORE_INFINITY,
270 : replica->ip, replica->container, NULL, NULL,
271 : pcmk__coloc_influence);
272 : }
273 :
274 0 : 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 0 : replica->remote->cmds->internal_constraints(replica->remote);
281 : }
282 :
283 0 : if (replica->child != NULL) {
284 0 : CRM_ASSERT(replica->remote != NULL);
285 : // "Start remote then child" is implicit in scheduler's remote logic
286 : }
287 0 : 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 0 : pcmk__bundle_internal_constraints(pcmk_resource_t *rsc)
298 : {
299 0 : pcmk_resource_t *bundled_resource = NULL;
300 :
301 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
302 :
303 0 : pe__foreach_bundle_replica(rsc, replica_internal_constraints, rsc);
304 :
305 0 : bundled_resource = pe__bundled_resource(rsc);
306 0 : if (bundled_resource == NULL) {
307 0 : return;
308 : }
309 :
310 : // Start bundle -> start bundled clone
311 0 : 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 0 : 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 0 : 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 0 : pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_STOPPED,
327 : rsc, PCMK_ACTION_STOPPED,
328 : pcmk__ar_first_implies_then_graphed);
329 :
330 0 : bundled_resource->cmds->internal_constraints(bundled_resource);
331 :
332 0 : if (!pcmk_is_set(bundled_resource->flags, pcmk_rsc_promotable)) {
333 0 : return;
334 : }
335 0 : pcmk__promotable_restart_ordering(rsc);
336 :
337 : // Demote bundle -> demote bundled clone
338 0 : 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 0 : 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 0 : 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 0 : 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 0 : match_replica_container(const pcmk__bundle_replica_t *replica, void *user_data)
375 : {
376 0 : struct match_data *match_data = user_data;
377 :
378 0 : if (pcmk__instance_matches(replica->container, match_data->node,
379 : pcmk_role_unknown, false)) {
380 0 : match_data->container = replica->container;
381 0 : return false; // Match found, don't bother searching further replicas
382 : }
383 0 : 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 0 : get_bundle_node_host(const pcmk_node_t *node)
397 : {
398 0 : if (pcmk__is_bundle_node(node)) {
399 0 : const pcmk_resource_t *container = node->details->remote_rsc->container;
400 :
401 0 : return container->fns->location(container, NULL, 0);
402 : }
403 0 : 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 0 : compatible_container(const pcmk_resource_t *dependent,
419 : const pcmk_resource_t *bundle)
420 : {
421 0 : GList *scratch = NULL;
422 0 : struct match_data match_data = { NULL, NULL };
423 :
424 : // If dependent is assigned, only check there
425 0 : match_data.node = dependent->fns->location(dependent, NULL, 0);
426 0 : match_data.node = get_bundle_node_host(match_data.node);
427 0 : if (match_data.node != NULL) {
428 0 : pe__foreach_const_bundle_replica(bundle, match_replica_container,
429 : &match_data);
430 0 : return match_data.container;
431 : }
432 :
433 : // Otherwise, check for any of the dependent's allowed nodes
434 0 : scratch = g_hash_table_get_values(dependent->allowed_nodes);
435 0 : scratch = pcmk__sort_nodes(scratch, NULL);
436 0 : for (const GList *iter = scratch; iter != NULL; iter = iter->next) {
437 0 : match_data.node = iter->data;
438 0 : match_data.node = get_bundle_node_host(match_data.node);
439 0 : if (match_data.node == NULL) {
440 0 : continue;
441 : }
442 :
443 0 : pe__foreach_const_bundle_replica(bundle, match_replica_container,
444 : &match_data);
445 0 : if (match_data.container != NULL) {
446 0 : break;
447 : }
448 : }
449 0 : g_list_free(scratch);
450 0 : 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 0 : replica_apply_coloc_score(const pcmk__bundle_replica_t *replica,
470 : void *user_data)
471 : {
472 0 : struct coloc_data *coloc_data = user_data;
473 0 : pcmk_node_t *chosen = NULL;
474 :
475 0 : if (coloc_data->colocation->score < PCMK_SCORE_INFINITY) {
476 0 : replica->container->cmds->apply_coloc_score(coloc_data->dependent,
477 0 : replica->container,
478 : coloc_data->colocation,
479 : false);
480 0 : return true;
481 : }
482 :
483 0 : chosen = replica->container->fns->location(replica->container, NULL, 0);
484 0 : if ((chosen == NULL)
485 0 : || is_set_recursive(replica->container, pcmk_rsc_blocked, true)) {
486 0 : return true;
487 : }
488 :
489 0 : if ((coloc_data->colocation->primary_role >= pcmk_role_promoted)
490 0 : && ((replica->child == NULL)
491 0 : || (replica->child->next_role < pcmk_role_promoted))) {
492 0 : return true;
493 : }
494 :
495 0 : pcmk__rsc_trace(pe__const_top_resource(replica->container, true),
496 : "Allowing mandatory colocation %s using %s @%d",
497 : coloc_data->colocation->id, pcmk__node_name(chosen),
498 : chosen->weight);
499 0 : coloc_data->container_hosts = g_list_prepend(coloc_data->container_hosts,
500 : chosen);
501 0 : return true;
502 : }
503 :
504 : /*!
505 : * \internal
506 : * \brief Apply a colocation's score to node scores or resource priority
507 : *
508 : * Given a colocation constraint, apply its score to the dependent's
509 : * allowed node scores (if we are still placing resources) or priority (if
510 : * we are choosing promotable clone instance roles).
511 : *
512 : * \param[in,out] dependent Dependent resource in colocation
513 : * \param[in] primary Primary resource in colocation
514 : * \param[in] colocation Colocation constraint to apply
515 : * \param[in] for_dependent true if called on behalf of dependent
516 : */
517 : void
518 0 : pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent,
519 : const pcmk_resource_t *primary,
520 : const pcmk__colocation_t *colocation,
521 : bool for_dependent)
522 : {
523 0 : struct coloc_data coloc_data = { colocation, dependent, NULL };
524 :
525 : /* This should never be called for the bundle itself as a dependent.
526 : * Instead, we add its colocation constraints to its containers and bundled
527 : * primitive and call the apply_coloc_score() method for them as dependents.
528 : */
529 0 : CRM_ASSERT((primary != NULL)
530 : && (primary->variant == pcmk_rsc_variant_bundle)
531 : && (dependent != NULL)
532 : && (dependent->variant == pcmk_rsc_variant_primitive)
533 : && (colocation != NULL) && !for_dependent);
534 :
535 0 : if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
536 0 : pcmk__rsc_trace(primary,
537 : "Skipping applying colocation %s "
538 : "because %s is still provisional",
539 : colocation->id, primary->id);
540 0 : return;
541 : }
542 0 : pcmk__rsc_trace(primary, "Applying colocation %s (%s with %s at %s)",
543 : colocation->id, dependent->id, primary->id,
544 : pcmk_readable_score(colocation->score));
545 :
546 : /* If the constraint dependent is a clone or bundle, "dependent" here is one
547 : * of its instances. Look for a compatible instance of this bundle.
548 : */
549 0 : if (colocation->dependent->variant > pcmk_rsc_variant_group) {
550 0 : const pcmk_resource_t *primary_container = NULL;
551 :
552 0 : primary_container = compatible_container(dependent, primary);
553 0 : if (primary_container != NULL) { // Success, we found one
554 0 : pcmk__rsc_debug(primary, "Pairing %s with %s",
555 : dependent->id, primary_container->id);
556 0 : dependent->cmds->apply_coloc_score(dependent, primary_container,
557 : colocation, true);
558 :
559 0 : } else if (colocation->score >= PCMK_SCORE_INFINITY) {
560 : // Failure, and it's fatal
561 0 : crm_notice("%s cannot run because there is no compatible "
562 : "instance of %s to colocate with",
563 : dependent->id, primary->id);
564 0 : pcmk__assign_resource(dependent, NULL, true, true);
565 :
566 : } else { // Failure, but we can ignore it
567 0 : pcmk__rsc_debug(primary,
568 : "%s cannot be colocated with any instance of %s",
569 : dependent->id, primary->id);
570 : }
571 0 : return;
572 : }
573 :
574 0 : pe__foreach_const_bundle_replica(primary, replica_apply_coloc_score,
575 : &coloc_data);
576 :
577 0 : if (colocation->score >= PCMK_SCORE_INFINITY) {
578 0 : pcmk__colocation_intersect_nodes(dependent, primary, colocation,
579 0 : coloc_data.container_hosts, false);
580 : }
581 0 : g_list_free(coloc_data.container_hosts);
582 : }
583 :
584 : // Bundle implementation of pcmk_assignment_methods_t:with_this_colocations()
585 : void
586 0 : pcmk__with_bundle_colocations(const pcmk_resource_t *rsc,
587 : const pcmk_resource_t *orig_rsc, GList **list)
588 : {
589 0 : const pcmk_resource_t *bundled_rsc = NULL;
590 :
591 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
592 : && (orig_rsc != NULL) && (list != NULL));
593 :
594 : // The bundle itself and its containers always get its colocations
595 0 : if ((orig_rsc == rsc)
596 0 : || pcmk_is_set(orig_rsc->flags, pcmk_rsc_replica_container)) {
597 :
598 0 : pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
599 0 : return;
600 : }
601 :
602 : /* The bundled resource gets the colocations if it's promotable and we've
603 : * begun choosing roles
604 : */
605 0 : bundled_rsc = pe__bundled_resource(rsc);
606 0 : if ((bundled_rsc == NULL)
607 0 : || !pcmk_is_set(bundled_rsc->flags, pcmk_rsc_promotable)
608 0 : || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) {
609 0 : return;
610 : }
611 :
612 0 : if (orig_rsc == bundled_rsc) {
613 0 : if (pe__clone_flag_is_set(orig_rsc,
614 : pcmk__clone_promotion_constrained)) {
615 : /* orig_rsc is the clone and we're setting roles (or have already
616 : * done so)
617 : */
618 0 : pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
619 : }
620 :
621 0 : } else if (!pcmk_is_set(orig_rsc->flags, pcmk_rsc_unassigned)) {
622 : /* orig_rsc is an instance and is already assigned. If something
623 : * requests colocations for orig_rsc now, it's for setting roles.
624 : */
625 0 : pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
626 : }
627 : }
628 :
629 : // Bundle implementation of pcmk_assignment_methods_t:this_with_colocations()
630 : void
631 0 : pcmk__bundle_with_colocations(const pcmk_resource_t *rsc,
632 : const pcmk_resource_t *orig_rsc, GList **list)
633 : {
634 0 : const pcmk_resource_t *bundled_rsc = NULL;
635 :
636 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
637 : && (orig_rsc != NULL) && (list != NULL));
638 :
639 : // The bundle itself and its containers always get its colocations
640 0 : if ((orig_rsc == rsc)
641 0 : || pcmk_is_set(orig_rsc->flags, pcmk_rsc_replica_container)) {
642 :
643 0 : pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
644 0 : return;
645 : }
646 :
647 : /* The bundled resource gets the colocations if it's promotable and we've
648 : * begun choosing roles
649 : */
650 0 : bundled_rsc = pe__bundled_resource(rsc);
651 0 : if ((bundled_rsc == NULL)
652 0 : || !pcmk_is_set(bundled_rsc->flags, pcmk_rsc_promotable)
653 0 : || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) {
654 0 : return;
655 : }
656 :
657 0 : if (orig_rsc == bundled_rsc) {
658 0 : if (pe__clone_flag_is_set(orig_rsc,
659 : pcmk__clone_promotion_constrained)) {
660 : /* orig_rsc is the clone and we're setting roles (or have already
661 : * done so)
662 : */
663 0 : pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
664 : }
665 :
666 0 : } else if (!pcmk_is_set(orig_rsc->flags, pcmk_rsc_unassigned)) {
667 : /* orig_rsc is an instance and is already assigned. If something
668 : * requests colocations for orig_rsc now, it's for setting roles.
669 : */
670 0 : pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
671 : }
672 : }
673 :
674 : /*!
675 : * \internal
676 : * \brief Return action flags for a given bundle resource action
677 : *
678 : * \param[in,out] action Bundle resource action to get flags for
679 : * \param[in] node If not NULL, limit effects to this node
680 : *
681 : * \return Flags appropriate to \p action on \p node
682 : */
683 : uint32_t
684 0 : pcmk__bundle_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
685 : {
686 0 : GList *containers = NULL;
687 0 : uint32_t flags = 0;
688 0 : pcmk_resource_t *bundled_resource = NULL;
689 :
690 0 : CRM_ASSERT((action != NULL) && (action->rsc != NULL)
691 : && (action->rsc->variant == pcmk_rsc_variant_bundle));
692 :
693 0 : bundled_resource = pe__bundled_resource(action->rsc);
694 0 : if (bundled_resource != NULL) {
695 : // Clone actions are done on the bundled clone resource, not container
696 0 : switch (get_complex_task(bundled_resource, action->task)) {
697 0 : case pcmk_action_unspecified:
698 : case pcmk_action_notify:
699 : case pcmk_action_notified:
700 : case pcmk_action_promote:
701 : case pcmk_action_promoted:
702 : case pcmk_action_demote:
703 : case pcmk_action_demoted:
704 0 : return pcmk__collective_action_flags(action,
705 0 : bundled_resource->children,
706 : node);
707 0 : default:
708 0 : break;
709 : }
710 : }
711 :
712 0 : containers = pe__bundle_containers(action->rsc);
713 0 : flags = pcmk__collective_action_flags(action, containers, node);
714 0 : g_list_free(containers);
715 0 : return flags;
716 : }
717 :
718 : /*!
719 : * \internal
720 : * \brief Apply a location constraint to a bundle replica
721 : *
722 : * \param[in,out] replica Replica to apply constraint to
723 : * \param[in,out] user_data Location constraint to apply
724 : *
725 : * \return true (to indicate that any further replicas should be processed)
726 : */
727 : static bool
728 0 : apply_location_to_replica(pcmk__bundle_replica_t *replica, void *user_data)
729 : {
730 0 : pcmk__location_t *location = user_data;
731 :
732 0 : if (replica->container != NULL) {
733 0 : replica->container->cmds->apply_location(replica->container, location);
734 : }
735 0 : if (replica->ip != NULL) {
736 0 : replica->ip->cmds->apply_location(replica->ip, location);
737 : }
738 0 : return true;
739 : }
740 :
741 : /*!
742 : * \internal
743 : * \brief Apply a location constraint to a bundle resource's allowed node scores
744 : *
745 : * \param[in,out] rsc Bundle resource to apply constraint to
746 : * \param[in,out] location Location constraint to apply
747 : */
748 : void
749 0 : pcmk__bundle_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
750 : {
751 0 : pcmk_resource_t *bundled_resource = NULL;
752 :
753 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
754 : && (location != NULL));
755 :
756 0 : pcmk__apply_location(rsc, location);
757 0 : pe__foreach_bundle_replica(rsc, apply_location_to_replica, location);
758 :
759 0 : bundled_resource = pe__bundled_resource(rsc);
760 0 : if ((bundled_resource != NULL)
761 0 : && ((location->role_filter == pcmk_role_unpromoted)
762 0 : || (location->role_filter == pcmk_role_promoted))) {
763 0 : bundled_resource->cmds->apply_location(bundled_resource, location);
764 0 : bundled_resource->rsc_location = g_list_prepend(
765 : bundled_resource->rsc_location, location);
766 : }
767 0 : }
768 :
769 : #define XPATH_REMOTE "//nvpair[@name='" PCMK_REMOTE_RA_ADDR "']"
770 :
771 : /*!
772 : * \internal
773 : * \brief Add a bundle replica's actions to transition graph
774 : *
775 : * \param[in,out] replica Replica to add to graph
776 : * \param[in] user_data Bundle that replica belongs to (for logging only)
777 : *
778 : * \return true (to indicate that any further replicas should be processed)
779 : */
780 : static bool
781 0 : add_replica_actions_to_graph(pcmk__bundle_replica_t *replica, void *user_data)
782 : {
783 0 : if ((replica->remote != NULL) && (replica->container != NULL)
784 0 : && pe__bundle_needs_remote_name(replica->remote)) {
785 :
786 : /* REMOTE_CONTAINER_HACK: Allow remote nodes to run containers that
787 : * run pacemaker-remoted inside, without needing a separate IP for
788 : * the container. This is done by configuring the inner remote's
789 : * connection host as the magic string "#uname", then
790 : * replacing it with the underlying host when needed.
791 : */
792 0 : xmlNode *nvpair = get_xpath_object(XPATH_REMOTE, replica->remote->xml,
793 : LOG_ERR);
794 0 : const char *calculated_addr = NULL;
795 :
796 : // Replace the value in replica->remote->xml (if appropriate)
797 0 : calculated_addr = pe__add_bundle_remote_name(replica->remote, nvpair,
798 : PCMK_XA_VALUE);
799 0 : if (calculated_addr != NULL) {
800 : /* Since this is for the bundle as a resource, and not any
801 : * particular action, replace the value in the default
802 : * parameters (not evaluated for node). create_graph_action()
803 : * will grab it from there to replace it in node-evaluated
804 : * parameters.
805 : */
806 0 : GHashTable *params = pe_rsc_params(replica->remote,
807 0 : NULL, replica->remote->cluster);
808 :
809 0 : pcmk__insert_dup(params, PCMK_REMOTE_RA_ADDR, calculated_addr);
810 : } else {
811 0 : pcmk_resource_t *bundle = user_data;
812 :
813 : /* The only way to get here is if the remote connection is
814 : * neither currently running nor scheduled to run. That means we
815 : * won't be doing any operations that require addr (only start
816 : * requires it; we additionally use it to compare digests when
817 : * unpacking status, promote, and migrate_from history, but
818 : * that's already happened by this point).
819 : */
820 0 : pcmk__rsc_info(bundle,
821 : "Unable to determine address for bundle %s "
822 : "remote connection", bundle->id);
823 : }
824 : }
825 0 : if (replica->ip != NULL) {
826 0 : replica->ip->cmds->add_actions_to_graph(replica->ip);
827 : }
828 0 : if (replica->container != NULL) {
829 0 : replica->container->cmds->add_actions_to_graph(replica->container);
830 : }
831 0 : if (replica->remote != NULL) {
832 0 : replica->remote->cmds->add_actions_to_graph(replica->remote);
833 : }
834 0 : return true;
835 : }
836 :
837 : /*!
838 : * \internal
839 : * \brief Add a bundle resource's actions to the transition graph
840 : *
841 : * \param[in,out] rsc Bundle resource whose actions should be added
842 : */
843 : void
844 0 : pcmk__bundle_add_actions_to_graph(pcmk_resource_t *rsc)
845 : {
846 0 : pcmk_resource_t *bundled_resource = NULL;
847 :
848 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
849 :
850 0 : bundled_resource = pe__bundled_resource(rsc);
851 0 : if (bundled_resource != NULL) {
852 0 : bundled_resource->cmds->add_actions_to_graph(bundled_resource);
853 : }
854 0 : pe__foreach_bundle_replica(rsc, add_replica_actions_to_graph, rsc);
855 0 : }
856 :
857 : struct probe_data {
858 : pcmk_resource_t *bundle; // Bundle being probed
859 : pcmk_node_t *node; // Node to create probes on
860 : bool any_created; // Whether any probes have been created
861 : };
862 :
863 : /*!
864 : * \internal
865 : * \brief Order a bundle replica's start after another replica's probe
866 : *
867 : * \param[in,out] replica Replica to order start for
868 : * \param[in,out] user_data Replica with probe to order after
869 : *
870 : * \return true (to indicate that any further replicas should be processed)
871 : */
872 : static bool
873 0 : order_replica_start_after(pcmk__bundle_replica_t *replica, void *user_data)
874 : {
875 0 : pcmk__bundle_replica_t *probed_replica = user_data;
876 :
877 0 : if ((replica == probed_replica) || (replica->container == NULL)) {
878 0 : return true;
879 : }
880 0 : pcmk__new_ordering(probed_replica->container,
881 0 : pcmk__op_key(probed_replica->container->id,
882 : PCMK_ACTION_MONITOR, 0),
883 : NULL, replica->container,
884 0 : pcmk__op_key(replica->container->id, PCMK_ACTION_START,
885 : 0),
886 : NULL, pcmk__ar_ordered|pcmk__ar_if_on_same_node,
887 0 : replica->container->cluster);
888 0 : return true;
889 : }
890 :
891 : /*!
892 : * \internal
893 : * \brief Create probes for a bundle replica's resources
894 : *
895 : * \param[in,out] replica Replica to create probes for
896 : * \param[in,out] user_data struct probe_data
897 : *
898 : * \return true (to indicate that any further replicas should be processed)
899 : */
900 : static bool
901 0 : create_replica_probes(pcmk__bundle_replica_t *replica, void *user_data)
902 : {
903 0 : struct probe_data *probe_data = user_data;
904 :
905 0 : if ((replica->ip != NULL)
906 0 : && replica->ip->cmds->create_probe(replica->ip, probe_data->node)) {
907 0 : probe_data->any_created = true;
908 : }
909 0 : if ((replica->child != NULL)
910 0 : && pcmk__same_node(probe_data->node, replica->node)
911 0 : && replica->child->cmds->create_probe(replica->child,
912 : probe_data->node)) {
913 0 : probe_data->any_created = true;
914 : }
915 0 : if ((replica->container != NULL)
916 0 : && replica->container->cmds->create_probe(replica->container,
917 : probe_data->node)) {
918 0 : probe_data->any_created = true;
919 :
920 : /* If we're limited to one replica per host (due to
921 : * the lack of an IP range probably), then we don't
922 : * want any of our peer containers starting until
923 : * we've established that no other copies are already
924 : * running.
925 : *
926 : * Partly this is to ensure that the maximum replicas per host is
927 : * observed, but also to ensure that the containers
928 : * don't fail to start because the necessary port
929 : * mappings (which won't include an IP for uniqueness)
930 : * are already taken
931 : */
932 0 : if (probe_data->bundle->fns->max_per_node(probe_data->bundle) == 1) {
933 0 : pe__foreach_bundle_replica(probe_data->bundle,
934 : order_replica_start_after, replica);
935 : }
936 : }
937 0 : if ((replica->container != NULL) && (replica->remote != NULL)
938 0 : && replica->remote->cmds->create_probe(replica->remote,
939 : probe_data->node)) {
940 : /* Do not probe the remote resource until we know where the container is
941 : * running. This is required for REMOTE_CONTAINER_HACK to correctly
942 : * probe remote resources.
943 : */
944 0 : char *probe_uuid = pcmk__op_key(replica->remote->id,
945 : PCMK_ACTION_MONITOR, 0);
946 0 : pcmk_action_t *probe = find_first_action(replica->remote->actions,
947 : probe_uuid, NULL,
948 0 : probe_data->node);
949 :
950 0 : free(probe_uuid);
951 0 : if (probe != NULL) {
952 0 : probe_data->any_created = true;
953 0 : pcmk__rsc_trace(probe_data->bundle, "Ordering %s probe on %s",
954 : replica->remote->id,
955 : pcmk__node_name(probe_data->node));
956 0 : pcmk__new_ordering(replica->container,
957 0 : pcmk__op_key(replica->container->id,
958 : PCMK_ACTION_START, 0),
959 : NULL, replica->remote, NULL, probe,
960 : pcmk__ar_nested_remote_probe,
961 0 : probe_data->bundle->cluster);
962 : }
963 : }
964 0 : 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 0 : pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node)
979 : {
980 0 : struct probe_data probe_data = { rsc, node, false };
981 :
982 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
983 0 : pe__foreach_bundle_replica(rsc, create_replica_probes, &probe_data);
984 0 : 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 0 : output_replica_actions(pcmk__bundle_replica_t *replica, void *user_data)
998 : {
999 0 : if (replica->ip != NULL) {
1000 0 : replica->ip->cmds->output_actions(replica->ip);
1001 : }
1002 0 : if (replica->container != NULL) {
1003 0 : replica->container->cmds->output_actions(replica->container);
1004 : }
1005 0 : if (replica->remote != NULL) {
1006 0 : replica->remote->cmds->output_actions(replica->remote);
1007 : }
1008 0 : if (replica->child != NULL) {
1009 0 : replica->child->cmds->output_actions(replica->child);
1010 : }
1011 0 : 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 0 : pcmk__output_bundle_actions(pcmk_resource_t *rsc)
1022 : {
1023 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
1024 0 : pe__foreach_bundle_replica(rsc, output_replica_actions, NULL);
1025 0 : }
1026 :
1027 : // Bundle implementation of pcmk_assignment_methods_t:add_utilization()
1028 : void
1029 0 : pcmk__bundle_add_utilization(const pcmk_resource_t *rsc,
1030 : const pcmk_resource_t *orig_rsc, GList *all_rscs,
1031 : GHashTable *utilization)
1032 : {
1033 0 : pcmk_resource_t *container = NULL;
1034 :
1035 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
1036 :
1037 0 : if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
1038 0 : 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 0 : container = pe__first_container(rsc);
1046 0 : if (container != NULL) {
1047 0 : 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 0 : pcmk__bundle_shutdown_lock(pcmk_resource_t *rsc)
1055 : {
1056 0 : CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
1057 : // Bundles currently don't support shutdown locks
1058 0 : }
|