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 <crm/common/xml.h>
13 : #include <pacemaker-internal.h>
14 :
15 : #include "libpacemaker_private.h"
16 :
17 : /*!
18 : * \internal
19 : * \brief Assign a clone resource's instances to nodes
20 : *
21 : * \param[in,out] rsc Clone resource to assign
22 : * \param[in] prefer Node to prefer, if all else is equal
23 : * \param[in] stop_if_fail If \c true and a primitive descendant of \p rsc
24 : * can't be assigned to a node, set the
25 : * descendant's next role to stopped and update
26 : * existing actions
27 : *
28 : * \return NULL (clones are not assigned to a single node)
29 : *
30 : * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
31 : * completely undo the assignment. A successful assignment can be either
32 : * undone or left alone as final. A failed assignment has the same effect
33 : * as calling pcmk__unassign_resource(); there are no side effects on
34 : * roles or actions.
35 : */
36 : pcmk_node_t *
37 0 : pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
38 : bool stop_if_fail)
39 : {
40 0 : GList *colocations = NULL;
41 :
42 0 : CRM_ASSERT(pcmk__is_clone(rsc));
43 :
44 0 : if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
45 0 : return NULL; // Assignment has already been done
46 : }
47 :
48 : // Detect assignment loops
49 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) {
50 0 : pcmk__rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id);
51 0 : return NULL;
52 : }
53 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning);
54 :
55 : // If this clone is promotable, consider nodes' promotion scores
56 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
57 0 : pcmk__add_promotion_scores(rsc);
58 : }
59 :
60 : // If this clone is colocated with any other resources, assign those first
61 0 : colocations = pcmk__this_with_colocations(rsc);
62 0 : for (GList *iter = colocations; iter != NULL; iter = iter->next) {
63 0 : pcmk__colocation_t *constraint = (pcmk__colocation_t *) iter->data;
64 :
65 0 : pcmk__rsc_trace(rsc, "%s: Assigning colocation %s primary %s first",
66 : rsc->id, constraint->id, constraint->primary->id);
67 0 : constraint->primary->cmds->assign(constraint->primary, prefer,
68 : stop_if_fail);
69 : }
70 0 : g_list_free(colocations);
71 :
72 : // If any resources are colocated with this one, consider their preferences
73 0 : colocations = pcmk__with_this_colocations(rsc);
74 0 : g_list_foreach(colocations, pcmk__add_dependent_scores, rsc);
75 0 : g_list_free(colocations);
76 :
77 0 : pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags,
78 : pcmk_sched_output_scores),
79 : rsc, __func__, rsc->allowed_nodes, rsc->cluster);
80 :
81 0 : rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance);
82 0 : pcmk__assign_instances(rsc, rsc->children, pe__clone_max(rsc),
83 : pe__clone_node_max(rsc));
84 :
85 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
86 0 : pcmk__set_instance_roles(rsc);
87 : }
88 :
89 0 : pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned|pcmk_rsc_assigning);
90 0 : pcmk__rsc_trace(rsc, "Assigned clone %s", rsc->id);
91 0 : return NULL;
92 : }
93 :
94 : /*!
95 : * \internal
96 : * \brief Create all actions needed for a given clone resource
97 : *
98 : * \param[in,out] rsc Clone resource to create actions for
99 : */
100 : void
101 0 : pcmk__clone_create_actions(pcmk_resource_t *rsc)
102 : {
103 0 : CRM_ASSERT(pcmk__is_clone(rsc));
104 :
105 0 : pcmk__rsc_trace(rsc, "Creating actions for clone %s", rsc->id);
106 0 : pcmk__create_instance_actions(rsc, rsc->children);
107 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
108 0 : pcmk__create_promotable_actions(rsc);
109 : }
110 0 : }
111 :
112 : /*!
113 : * \internal
114 : * \brief Create implicit constraints needed for a clone resource
115 : *
116 : * \param[in,out] rsc Clone resource to create implicit constraints for
117 : */
118 : void
119 0 : pcmk__clone_internal_constraints(pcmk_resource_t *rsc)
120 : {
121 0 : bool ordered = false;
122 :
123 0 : CRM_ASSERT(pcmk__is_clone(rsc));
124 :
125 0 : pcmk__rsc_trace(rsc, "Creating internal constraints for clone %s", rsc->id);
126 :
127 : // Restart ordering: Stop -> stopped -> start -> started
128 0 : pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
129 : rsc, PCMK_ACTION_START,
130 : pcmk__ar_ordered);
131 0 : pcmk__order_resource_actions(rsc, PCMK_ACTION_START,
132 : rsc, PCMK_ACTION_RUNNING,
133 : pcmk__ar_unrunnable_first_blocks);
134 0 : pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP,
135 : rsc, PCMK_ACTION_STOPPED,
136 : pcmk__ar_unrunnable_first_blocks);
137 :
138 : // Demoted -> stop and started -> promote
139 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
140 0 : pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
141 : rsc, PCMK_ACTION_STOP,
142 : pcmk__ar_ordered);
143 0 : pcmk__order_resource_actions(rsc, PCMK_ACTION_RUNNING,
144 : rsc, PCMK_ACTION_PROMOTE,
145 : pcmk__ar_unrunnable_first_blocks);
146 : }
147 :
148 0 : ordered = pe__clone_is_ordered(rsc);
149 0 : if (ordered) {
150 : /* Ordered clone instances must start and stop by instance number. The
151 : * instances might have been previously shuffled for assignment or
152 : * promotion purposes, so re-sort them.
153 : */
154 0 : rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
155 : }
156 0 : for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
157 0 : pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
158 :
159 0 : instance->cmds->internal_constraints(instance);
160 :
161 : // Start clone -> start instance -> clone started
162 0 : pcmk__order_starts(rsc, instance, pcmk__ar_unrunnable_first_blocks
163 : |pcmk__ar_then_implies_first_graphed);
164 0 : pcmk__order_resource_actions(instance, PCMK_ACTION_START,
165 : rsc, PCMK_ACTION_RUNNING,
166 : pcmk__ar_first_implies_then_graphed);
167 :
168 : // Stop clone -> stop instance -> clone stopped
169 0 : pcmk__order_stops(rsc, instance, pcmk__ar_then_implies_first_graphed);
170 0 : pcmk__order_resource_actions(instance, PCMK_ACTION_STOP,
171 : rsc, PCMK_ACTION_STOPPED,
172 : pcmk__ar_first_implies_then_graphed);
173 :
174 : /* Instances of ordered clones must be started and stopped by instance
175 : * number. Since only some instances may be starting or stopping, order
176 : * each instance relative to every later instance.
177 : */
178 0 : if (ordered) {
179 0 : for (GList *later = iter->next;
180 0 : later != NULL; later = later->next) {
181 0 : pcmk__order_starts(instance, (pcmk_resource_t *) later->data,
182 : pcmk__ar_ordered);
183 0 : pcmk__order_stops((pcmk_resource_t *) later->data, instance,
184 : pcmk__ar_ordered);
185 : }
186 : }
187 : }
188 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
189 0 : pcmk__order_promotable_instances(rsc);
190 : }
191 0 : }
192 :
193 : /*!
194 : * \internal
195 : * \brief Check whether colocated resources can be interleaved
196 : *
197 : * \param[in] colocation Colocation constraint with clone as primary
198 : *
199 : * \return true if colocated resources can be interleaved, otherwise false
200 : */
201 : static bool
202 0 : can_interleave(const pcmk__colocation_t *colocation)
203 : {
204 0 : const pcmk_resource_t *dependent = colocation->dependent;
205 :
206 : // Only colocations between clone or bundle resources use interleaving
207 0 : if (dependent->variant <= pcmk_rsc_variant_group) {
208 0 : return false;
209 : }
210 :
211 : // Only the dependent needs to be marked for interleaving
212 0 : if (!crm_is_true(g_hash_table_lookup(dependent->meta,
213 : PCMK_META_INTERLEAVE))) {
214 0 : return false;
215 : }
216 :
217 : /* @TODO Do we actually care about multiple primary instances sharing a
218 : * dependent instance?
219 : */
220 0 : if (dependent->fns->max_per_node(dependent)
221 0 : != colocation->primary->fns->max_per_node(colocation->primary)) {
222 0 : pcmk__config_err("Cannot interleave %s and %s because they do not "
223 : "support the same number of instances per node",
224 : dependent->id, colocation->primary->id);
225 0 : return false;
226 : }
227 :
228 0 : return true;
229 : }
230 :
231 : /*!
232 : * \internal
233 : * \brief Apply a colocation's score to node scores or resource priority
234 : *
235 : * Given a colocation constraint, apply its score to the dependent's
236 : * allowed node scores (if we are still placing resources) or priority (if
237 : * we are choosing promotable clone instance roles).
238 : *
239 : * \param[in,out] dependent Dependent resource in colocation
240 : * \param[in] primary Primary resource in colocation
241 : * \param[in] colocation Colocation constraint to apply
242 : * \param[in] for_dependent true if called on behalf of dependent
243 : */
244 : void
245 0 : pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent,
246 : const pcmk_resource_t *primary,
247 : const pcmk__colocation_t *colocation,
248 : bool for_dependent)
249 : {
250 0 : const GList *iter = NULL;
251 :
252 : /* This should never be called for the clone itself as a dependent. Instead,
253 : * we add its colocation constraints to its instances and call the
254 : * apply_coloc_score() method for the instances as dependents.
255 : */
256 0 : CRM_ASSERT(!for_dependent);
257 :
258 0 : CRM_ASSERT((colocation != NULL) && pcmk__is_clone(primary)
259 : && (dependent != NULL)
260 : && (dependent->variant == pcmk_rsc_variant_primitive));
261 :
262 0 : if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
263 0 : pcmk__rsc_trace(primary,
264 : "Delaying processing colocation %s "
265 : "because cloned primary %s is still provisional",
266 : colocation->id, primary->id);
267 0 : return;
268 : }
269 :
270 0 : pcmk__rsc_trace(primary, "Processing colocation %s (%s with clone %s @%s)",
271 : colocation->id, dependent->id, primary->id,
272 : pcmk_readable_score(colocation->score));
273 :
274 : // Apply role-specific colocations
275 0 : if (pcmk_is_set(primary->flags, pcmk_rsc_promotable)
276 0 : && (colocation->primary_role != pcmk_role_unknown)) {
277 :
278 0 : if (pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
279 : // We're assigning the dependent to a node
280 0 : pcmk__update_dependent_with_promotable(primary, dependent,
281 : colocation);
282 0 : return;
283 : }
284 :
285 0 : if (colocation->dependent_role == pcmk_role_promoted) {
286 : // We're choosing a role for the dependent
287 0 : pcmk__update_promotable_dependent_priority(primary, dependent,
288 : colocation);
289 0 : return;
290 : }
291 : }
292 :
293 : // Apply interleaved colocations
294 0 : if (can_interleave(colocation)) {
295 0 : const pcmk_resource_t *primary_instance = NULL;
296 :
297 0 : primary_instance = pcmk__find_compatible_instance(dependent, primary,
298 : pcmk_role_unknown,
299 : false);
300 0 : if (primary_instance != NULL) {
301 0 : pcmk__rsc_debug(primary, "Interleaving %s with %s",
302 : dependent->id, primary_instance->id);
303 0 : dependent->cmds->apply_coloc_score(dependent, primary_instance,
304 : colocation, true);
305 :
306 0 : } else if (colocation->score >= PCMK_SCORE_INFINITY) {
307 0 : crm_notice("%s cannot run because it cannot interleave with "
308 : "any instance of %s", dependent->id, primary->id);
309 0 : pcmk__assign_resource(dependent, NULL, true, true);
310 :
311 : } else {
312 0 : pcmk__rsc_debug(primary,
313 : "%s will not colocate with %s "
314 : "because no instance can interleave with it",
315 : dependent->id, primary->id);
316 : }
317 :
318 0 : return;
319 : }
320 :
321 : // Apply mandatory colocations
322 0 : if (colocation->score >= PCMK_SCORE_INFINITY) {
323 0 : GList *primary_nodes = NULL;
324 :
325 : // Dependent can run only where primary will have unblocked instances
326 0 : for (iter = primary->children; iter != NULL; iter = iter->next) {
327 0 : const pcmk_resource_t *instance = iter->data;
328 0 : pcmk_node_t *chosen = instance->fns->location(instance, NULL, 0);
329 :
330 0 : if ((chosen != NULL)
331 0 : && !is_set_recursive(instance, pcmk_rsc_blocked, TRUE)) {
332 0 : pcmk__rsc_trace(primary, "Allowing %s: %s %d",
333 : colocation->id, pcmk__node_name(chosen),
334 : chosen->weight);
335 0 : primary_nodes = g_list_prepend(primary_nodes, chosen);
336 : }
337 : }
338 0 : pcmk__colocation_intersect_nodes(dependent, primary, colocation,
339 : primary_nodes, false);
340 0 : g_list_free(primary_nodes);
341 0 : return;
342 : }
343 :
344 : // Apply optional colocations
345 0 : for (iter = primary->children; iter != NULL; iter = iter->next) {
346 0 : const pcmk_resource_t *instance = iter->data;
347 :
348 0 : instance->cmds->apply_coloc_score(dependent, instance, colocation,
349 : false);
350 : }
351 : }
352 :
353 : // Clone implementation of pcmk_assignment_methods_t:with_this_colocations()
354 : void
355 0 : pcmk__with_clone_colocations(const pcmk_resource_t *rsc,
356 : const pcmk_resource_t *orig_rsc, GList **list)
357 : {
358 0 : CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return);
359 :
360 0 : pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
361 :
362 0 : if (rsc->parent != NULL) {
363 0 : rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc, list);
364 : }
365 : }
366 :
367 : // Clone implementation of pcmk_assignment_methods_t:this_with_colocations()
368 : void
369 0 : pcmk__clone_with_colocations(const pcmk_resource_t *rsc,
370 : const pcmk_resource_t *orig_rsc, GList **list)
371 : {
372 0 : CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return);
373 :
374 0 : pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
375 :
376 0 : if (rsc->parent != NULL) {
377 0 : rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc, list);
378 : }
379 : }
380 :
381 : /*!
382 : * \internal
383 : * \brief Return action flags for a given clone resource action
384 : *
385 : * \param[in,out] action Action to get flags for
386 : * \param[in] node If not NULL, limit effects to this node
387 : *
388 : * \return Flags appropriate to \p action on \p node
389 : */
390 : uint32_t
391 0 : pcmk__clone_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
392 : {
393 0 : CRM_ASSERT((action != NULL) && pcmk__is_clone(action->rsc));
394 :
395 0 : return pcmk__collective_action_flags(action, action->rsc->children, node);
396 : }
397 :
398 : /*!
399 : * \internal
400 : * \brief Apply a location constraint to a clone resource's allowed node scores
401 : *
402 : * \param[in,out] rsc Clone resource to apply constraint to
403 : * \param[in,out] location Location constraint to apply
404 : */
405 : void
406 0 : pcmk__clone_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
407 : {
408 0 : CRM_CHECK((location != NULL) && pcmk__is_clone(rsc), return);
409 :
410 0 : pcmk__apply_location(rsc, location);
411 :
412 0 : for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
413 0 : pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
414 :
415 0 : instance->cmds->apply_location(instance, location);
416 : }
417 : }
418 :
419 : // GFunc wrapper for calling the action_flags() resource method
420 : static void
421 0 : call_action_flags(gpointer data, gpointer user_data)
422 : {
423 0 : pcmk_resource_t *rsc = user_data;
424 :
425 0 : rsc->cmds->action_flags((pcmk_action_t *) data, NULL);
426 0 : }
427 :
428 : /*!
429 : * \internal
430 : * \brief Add a clone resource's actions to the transition graph
431 : *
432 : * \param[in,out] rsc Resource whose actions should be added
433 : */
434 : void
435 0 : pcmk__clone_add_actions_to_graph(pcmk_resource_t *rsc)
436 : {
437 0 : CRM_ASSERT(pcmk__is_clone(rsc));
438 :
439 0 : g_list_foreach(rsc->actions, call_action_flags, rsc);
440 0 : pe__create_clone_notifications(rsc);
441 :
442 0 : for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
443 0 : pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
444 :
445 0 : child_rsc->cmds->add_actions_to_graph(child_rsc);
446 : }
447 :
448 0 : pcmk__add_rsc_actions_to_graph(rsc);
449 0 : pe__free_clone_notification_data(rsc);
450 0 : }
451 :
452 : /*!
453 : * \internal
454 : * \brief Check whether a resource or any children have been probed on a node
455 : *
456 : * \param[in] rsc Resource to check
457 : * \param[in] node Node to check
458 : *
459 : * \return true if \p node is in the known_on table of \p rsc or any of its
460 : * children, otherwise false
461 : */
462 : static bool
463 0 : rsc_probed_on(const pcmk_resource_t *rsc, const pcmk_node_t *node)
464 : {
465 0 : if (rsc->children != NULL) {
466 0 : for (GList *child_iter = rsc->children; child_iter != NULL;
467 0 : child_iter = child_iter->next) {
468 :
469 0 : pcmk_resource_t *child = (pcmk_resource_t *) child_iter->data;
470 :
471 0 : if (rsc_probed_on(child, node)) {
472 0 : return true;
473 : }
474 : }
475 0 : return false;
476 : }
477 :
478 0 : if (rsc->known_on != NULL) {
479 : GHashTableIter iter;
480 0 : pcmk_node_t *known_node = NULL;
481 :
482 0 : g_hash_table_iter_init(&iter, rsc->known_on);
483 0 : while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &known_node)) {
484 0 : if (pcmk__same_node(node, known_node)) {
485 0 : return true;
486 : }
487 : }
488 : }
489 0 : return false;
490 : }
491 :
492 : /*!
493 : * \internal
494 : * \brief Find clone instance that has been probed on given node
495 : *
496 : * \param[in] clone Clone resource to check
497 : * \param[in] node Node to check
498 : *
499 : * \return Instance of \p clone that has been probed on \p node if any,
500 : * otherwise NULL
501 : */
502 : static pcmk_resource_t *
503 0 : find_probed_instance_on(const pcmk_resource_t *clone, const pcmk_node_t *node)
504 : {
505 0 : for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
506 0 : pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
507 :
508 0 : if (rsc_probed_on(instance, node)) {
509 0 : return instance;
510 : }
511 : }
512 0 : return NULL;
513 : }
514 :
515 : /*!
516 : * \internal
517 : * \brief Probe an anonymous clone on a node
518 : *
519 : * \param[in,out] clone Anonymous clone to probe
520 : * \param[in,out] node Node to probe \p clone on
521 : */
522 : static bool
523 0 : probe_anonymous_clone(pcmk_resource_t *clone, pcmk_node_t *node)
524 : {
525 : // Check whether we already probed an instance on this node
526 0 : pcmk_resource_t *child = find_probed_instance_on(clone, node);
527 :
528 : // Otherwise, check if we plan to start an instance on this node
529 0 : for (GList *iter = clone->children; (iter != NULL) && (child == NULL);
530 0 : iter = iter->next) {
531 0 : pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
532 0 : const pcmk_node_t *instance_node = NULL;
533 :
534 0 : instance_node = instance->fns->location(instance, NULL, 0);
535 0 : if (pcmk__same_node(instance_node, node)) {
536 0 : child = instance;
537 : }
538 : }
539 :
540 : // Otherwise, use the first clone instance
541 0 : if (child == NULL) {
542 0 : child = clone->children->data;
543 : }
544 :
545 : // Anonymous clones only need to probe a single instance
546 0 : return child->cmds->create_probe(child, node);
547 : }
548 :
549 : /*!
550 : * \internal
551 : * \brief Schedule any probes needed for a resource on a node
552 : *
553 : * \param[in,out] rsc Resource to create probe for
554 : * \param[in,out] node Node to create probe on
555 : *
556 : * \return true if any probe was created, otherwise false
557 : */
558 : bool
559 0 : pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node)
560 : {
561 0 : CRM_ASSERT((node != NULL) && pcmk__is_clone(rsc));
562 :
563 0 : if (rsc->exclusive_discover) {
564 : /* The clone is configured to be probed only where a location constraint
565 : * exists with PCMK_XA_RESOURCE_DISCOVERY set to exclusive.
566 : *
567 : * This check is not strictly necessary here since the instance's
568 : * create_probe() method would also check, but doing it here is more
569 : * efficient (especially for unique clones with a large number of
570 : * instances), and affects the CRM_meta_notify_available_uname variable
571 : * passed with notify actions.
572 : */
573 0 : pcmk_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes,
574 0 : node->details->id);
575 :
576 0 : if ((allowed == NULL)
577 0 : || (allowed->rsc_discover_mode != pcmk_probe_exclusive)) {
578 : /* This node is not marked for resource discovery. Remove it from
579 : * allowed_nodes so that notifications contain only nodes that the
580 : * clone can possibly run on.
581 : */
582 0 : pcmk__rsc_trace(rsc,
583 : "Skipping probe for %s on %s because resource has "
584 : "exclusive discovery but is not allowed on node",
585 : rsc->id, pcmk__node_name(node));
586 0 : g_hash_table_remove(rsc->allowed_nodes, node->details->id);
587 0 : return false;
588 : }
589 : }
590 :
591 0 : rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
592 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
593 0 : return pcmk__probe_resource_list(rsc->children, node);
594 : } else {
595 0 : return probe_anonymous_clone(rsc, node);
596 : }
597 : }
598 :
599 : /*!
600 : * \internal
601 : * \brief Add meta-attributes relevant to transition graph actions to XML
602 : *
603 : * Add clone-specific meta-attributes needed for transition graph actions.
604 : *
605 : * \param[in] rsc Clone resource whose meta-attributes should be added
606 : * \param[in,out] xml Transition graph action attributes XML to add to
607 : */
608 : void
609 0 : pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
610 : {
611 0 : char *name = NULL;
612 :
613 0 : CRM_ASSERT(pcmk__is_clone(rsc) && (xml != NULL));
614 :
615 0 : name = crm_meta_name(PCMK_META_GLOBALLY_UNIQUE);
616 0 : crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
617 0 : free(name);
618 :
619 0 : name = crm_meta_name(PCMK_META_NOTIFY);
620 0 : crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_notify));
621 0 : free(name);
622 :
623 0 : name = crm_meta_name(PCMK_META_CLONE_MAX);
624 0 : crm_xml_add_int(xml, name, pe__clone_max(rsc));
625 0 : free(name);
626 :
627 0 : name = crm_meta_name(PCMK_META_CLONE_NODE_MAX);
628 0 : crm_xml_add_int(xml, name, pe__clone_node_max(rsc));
629 0 : free(name);
630 :
631 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
632 0 : int promoted_max = pe__clone_promoted_max(rsc);
633 0 : int promoted_node_max = pe__clone_promoted_node_max(rsc);
634 :
635 0 : name = crm_meta_name(PCMK_META_PROMOTED_MAX);
636 0 : crm_xml_add_int(xml, name, promoted_max);
637 0 : free(name);
638 :
639 0 : name = crm_meta_name(PCMK_META_PROMOTED_NODE_MAX);
640 0 : crm_xml_add_int(xml, name, promoted_node_max);
641 0 : free(name);
642 :
643 : /* @COMPAT Maintain backward compatibility with resource agents that
644 : * expect the old names (deprecated since 2.0.0).
645 : */
646 0 : name = crm_meta_name(PCMK__META_PROMOTED_MAX_LEGACY);
647 0 : crm_xml_add_int(xml, name, promoted_max);
648 0 : free(name);
649 :
650 0 : name = crm_meta_name(PCMK__META_PROMOTED_NODE_MAX_LEGACY);
651 0 : crm_xml_add_int(xml, name, promoted_node_max);
652 0 : free(name);
653 : }
654 0 : }
655 :
656 : // Clone implementation of pcmk_assignment_methods_t:add_utilization()
657 : void
658 0 : pcmk__clone_add_utilization(const pcmk_resource_t *rsc,
659 : const pcmk_resource_t *orig_rsc, GList *all_rscs,
660 : GHashTable *utilization)
661 : {
662 0 : bool existing = false;
663 0 : pcmk_resource_t *child = NULL;
664 :
665 0 : CRM_ASSERT(pcmk__is_clone(rsc) && (orig_rsc != NULL)
666 : && (utilization != NULL));
667 :
668 0 : if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
669 0 : return;
670 : }
671 :
672 : // Look for any child already existing in the list
673 0 : for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
674 0 : child = (pcmk_resource_t *) iter->data;
675 0 : if (g_list_find(all_rscs, child)) {
676 0 : existing = true; // Keep checking remaining children
677 : } else {
678 : // If this is a clone of a group, look for group's members
679 0 : for (GList *member_iter = child->children; member_iter != NULL;
680 0 : member_iter = member_iter->next) {
681 :
682 0 : pcmk_resource_t *member = (pcmk_resource_t *) member_iter->data;
683 :
684 0 : if (g_list_find(all_rscs, member) != NULL) {
685 : // Add *child's* utilization, not group member's
686 0 : child->cmds->add_utilization(child, orig_rsc, all_rscs,
687 : utilization);
688 0 : existing = true;
689 0 : break;
690 : }
691 : }
692 : }
693 : }
694 :
695 0 : if (!existing && (rsc->children != NULL)) {
696 : // If nothing was found, still add first child's utilization
697 0 : child = (pcmk_resource_t *) rsc->children->data;
698 :
699 0 : child->cmds->add_utilization(child, orig_rsc, all_rscs, utilization);
700 : }
701 : }
702 :
703 : // Clone implementation of pcmk_assignment_methods_t:shutdown_lock()
704 : void
705 0 : pcmk__clone_shutdown_lock(pcmk_resource_t *rsc)
706 : {
707 0 : CRM_ASSERT(pcmk__is_clone(rsc));
708 0 : return; // Clones currently don't support shutdown locks
709 : }
|