pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_sched_bundle.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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#define PE__VARIANT_BUNDLE 1
18#include <lib/pengine/variant.h>
19
20static bool
21is_bundle_node(pe__bundle_variant_data_t *data, pe_node_t *node)
22{
23 for (GList *gIter = data->replicas; gIter != NULL; gIter = gIter->next) {
24 pe__bundle_replica_t *replica = gIter->data;
25
26 if (node->details == replica->node->details) {
27 return TRUE;
28 }
29 }
30 return FALSE;
31}
32
33gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set);
34void distribute_children(pe_resource_t *rsc, GList *children, GList *nodes,
35 int max, int per_host_max, pe_working_set_t * data_set);
36
37static GList *
38get_container_list(pe_resource_t *rsc)
39{
40 GList *containers = NULL;
41
42 if (rsc->variant == pe_container) {
43 pe__bundle_variant_data_t *data = NULL;
44
45 get_bundle_variant_data(data, rsc);
46 for (GList *gIter = data->replicas; gIter != NULL;
47 gIter = gIter->next) {
48 pe__bundle_replica_t *replica = gIter->data;
49
50 containers = g_list_append(containers, replica->container);
51 }
52 }
53 return containers;
54}
55
56static inline GList *
57get_containers_or_children(pe_resource_t *rsc)
58{
59 return (rsc->variant == pe_container)?
60 get_container_list(rsc) : rsc->children;
61}
62
63static bool
64migration_threshold_reached(pe_resource_t *rsc, pe_node_t *node,
65 pe_working_set_t *data_set)
66{
67 int fail_count, countdown;
68
69 /* Migration threshold of 0 means never force away */
70 if (rsc->migration_threshold == 0) {
71 return FALSE;
72 }
73
74 // If we're ignoring failures, also ignore the migration threshold
76 return FALSE;
77 }
78
79 /* If there are no failures, there's no need to force away */
80 fail_count = pe_get_failcount(node, rsc, NULL,
82 data_set);
83 if (fail_count <= 0) {
84 return FALSE;
85 }
86
87 /* How many more times recovery will be tried on this node */
88 countdown = QB_MAX(rsc->migration_threshold - fail_count, 0);
89
90 if (countdown == 0) {
91 crm_warn("Forcing %s away from %s after %d failures (max=%d)",
92 rsc->id, node->details->uname, fail_count,
94 return TRUE;
95 }
96
97 crm_info("%s can fail %d more times on %s before being forced off",
98 rsc->id, countdown, node->details->uname);
99 return FALSE;
100}
101
102pe_node_t *
104 pe_working_set_t *data_set)
105{
106 GList *containers = NULL;
107 GList *nodes = NULL;
108 pe__bundle_variant_data_t *bundle_data = NULL;
109
110 CRM_CHECK(rsc != NULL, return NULL);
111
112 get_bundle_variant_data(bundle_data, rsc);
113
115 containers = get_container_list(rsc);
116
118 rsc, __func__, rsc->allowed_nodes, data_set);
119
120 nodes = g_hash_table_get_values(rsc->allowed_nodes);
121 nodes = sort_nodes_by_weight(nodes, NULL, data_set);
122 containers = g_list_sort_with_data(containers, sort_clone_instance, data_set);
123 distribute_children(rsc, containers, nodes, bundle_data->nreplicas,
124 bundle_data->nreplicas_per_host, data_set);
125 g_list_free(nodes);
126 g_list_free(containers);
127
128 for (GList *gIter = bundle_data->replicas; gIter != NULL;
129 gIter = gIter->next) {
130 pe__bundle_replica_t *replica = gIter->data;
131 pe_node_t *container_host = NULL;
132
133 CRM_ASSERT(replica);
134 if (replica->ip) {
135 pe_rsc_trace(rsc, "Allocating bundle %s IP %s",
136 rsc->id, replica->ip->id);
137 replica->ip->cmds->allocate(replica->ip, prefer, data_set);
138 }
139
140 container_host = replica->container->allocated_to;
141 if (replica->remote && pe__is_guest_or_remote_node(container_host)) {
142 /* We need 'nested' connection resources to be on the same
143 * host because pacemaker-remoted only supports a single
144 * active connection
145 */
146 pcmk__new_colocation("child-remote-with-docker-remote", NULL,
147 INFINITY, replica->remote,
148 container_host->details->remote_rsc, NULL,
149 NULL, true, data_set);
150 }
151
152 if (replica->remote) {
153 pe_rsc_trace(rsc, "Allocating bundle %s connection %s",
154 rsc->id, replica->remote->id);
155 replica->remote->cmds->allocate(replica->remote, prefer,
156 data_set);
157 }
158
159 // Explicitly allocate replicas' children before bundle child
160 if (replica->child) {
161 pe_node_t *node = NULL;
162 GHashTableIter iter;
163
164 g_hash_table_iter_init(&iter, replica->child->allowed_nodes);
165 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
166 if (node->details != replica->node->details) {
167 node->weight = -INFINITY;
168 } else if (!migration_threshold_reached(replica->child, node,
169 data_set)) {
170 node->weight = INFINITY;
171 }
172 }
173
174 pe__set_resource_flags(replica->child->parent, pe_rsc_allocating);
175 pe_rsc_trace(rsc, "Allocating bundle %s replica child %s",
176 rsc->id, replica->child->id);
177 replica->child->cmds->allocate(replica->child, replica->node,
178 data_set);
179 pe__clear_resource_flags(replica->child->parent,
181 }
182 }
183
184 if (bundle_data->child) {
185 pe_node_t *node = NULL;
186 GHashTableIter iter;
187 g_hash_table_iter_init(&iter, bundle_data->child->allowed_nodes);
188 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
189 if (is_bundle_node(bundle_data, node)) {
190 node->weight = 0;
191 } else {
192 node->weight = -INFINITY;
193 }
194 }
195 pe_rsc_trace(rsc, "Allocating bundle %s child %s",
196 rsc->id, bundle_data->child->id);
197 bundle_data->child->cmds->allocate(bundle_data->child, prefer, data_set);
198 }
199
201 return NULL;
202}
203
204
205void
207{
208 pe_action_t *action = NULL;
209 GList *containers = NULL;
210 pe__bundle_variant_data_t *bundle_data = NULL;
211
212 CRM_CHECK(rsc != NULL, return);
213
214 containers = get_container_list(rsc);
215 get_bundle_variant_data(bundle_data, rsc);
216 for (GList *gIter = bundle_data->replicas; gIter != NULL;
217 gIter = gIter->next) {
218 pe__bundle_replica_t *replica = gIter->data;
219
220 CRM_ASSERT(replica);
221 if (replica->ip) {
222 replica->ip->cmds->create_actions(replica->ip, data_set);
223 }
224 if (replica->container) {
225 replica->container->cmds->create_actions(replica->container,
226 data_set);
227 }
228 if (replica->remote) {
229 replica->remote->cmds->create_actions(replica->remote, data_set);
230 }
231 }
232
233 clone_create_pseudo_actions(rsc, containers, NULL, NULL, data_set);
234
235 if (bundle_data->child) {
236 bundle_data->child->cmds->create_actions(bundle_data->child, data_set);
237
238 if (pcmk_is_set(bundle_data->child->flags, pe_rsc_promotable)) {
239 /* promote */
240 create_pseudo_resource_op(rsc, RSC_PROMOTE, TRUE, TRUE, data_set);
241 action = create_pseudo_resource_op(rsc, RSC_PROMOTED, TRUE, TRUE, data_set);
242 action->priority = INFINITY;
243
244 /* demote */
245 create_pseudo_resource_op(rsc, RSC_DEMOTE, TRUE, TRUE, data_set);
246 action = create_pseudo_resource_op(rsc, RSC_DEMOTED, TRUE, TRUE, data_set);
247 action->priority = INFINITY;
248 }
249 }
250
251 g_list_free(containers);
252}
253
254void
256 pe_working_set_t *data_set)
257{
258 pe__bundle_variant_data_t *bundle_data = NULL;
259
260 CRM_CHECK(rsc != NULL, return);
261
262 get_bundle_variant_data(bundle_data, rsc);
263
264 if (bundle_data->child) {
265 new_rsc_order(rsc, RSC_START, bundle_data->child, RSC_START,
267 new_rsc_order(rsc, RSC_STOP, bundle_data->child, RSC_STOP,
269
270 if (bundle_data->child->children) {
271 new_rsc_order(bundle_data->child, RSC_STARTED, rsc, RSC_STARTED,
273 new_rsc_order(bundle_data->child, RSC_STOPPED, rsc, RSC_STOPPED,
275 } else {
276 new_rsc_order(bundle_data->child, RSC_START, rsc, RSC_STARTED,
278 new_rsc_order(bundle_data->child, RSC_STOP, rsc, RSC_STOPPED,
280 }
281 }
282
283 for (GList *gIter = bundle_data->replicas; gIter != NULL;
284 gIter = gIter->next) {
285 pe__bundle_replica_t *replica = gIter->data;
286
287 CRM_ASSERT(replica);
288 CRM_ASSERT(replica->container);
289
290 replica->container->cmds->internal_constraints(replica->container,
291 data_set);
292
293 order_start_start(rsc, replica->container,
295
296 if (replica->child) {
297 order_stop_stop(rsc, replica->child,
299 }
300 order_stop_stop(rsc, replica->container,
302 new_rsc_order(replica->container, RSC_START, rsc, RSC_STARTED,
304 new_rsc_order(replica->container, RSC_STOP, rsc, RSC_STOPPED,
306
307 if (replica->ip) {
308 replica->ip->cmds->internal_constraints(replica->ip, data_set);
309
310 // Start ip then container
311 new_rsc_order(replica->ip, RSC_START, replica->container, RSC_START,
313 new_rsc_order(replica->container, RSC_STOP, replica->ip, RSC_STOP,
315
316 pcmk__new_colocation("ip-with-docker", NULL, INFINITY, replica->ip,
317 replica->container, NULL, NULL, true,
318 data_set);
319 }
320
321 if (replica->remote) {
322 /* This handles ordering and colocating remote relative to container
323 * (via "resource-with-container"). Since IP is also ordered and
324 * colocated relative to the container, we don't need to do anything
325 * explicit here with IP.
326 */
327 replica->remote->cmds->internal_constraints(replica->remote,
328 data_set);
329 }
330
331 if (replica->child) {
332 CRM_ASSERT(replica->remote);
333
334 // "Start remote then child" is implicit in scheduler's remote logic
335 }
336
337 }
338
339 if (bundle_data->child) {
340 bundle_data->child->cmds->internal_constraints(bundle_data->child, data_set);
341 if (pcmk_is_set(bundle_data->child->flags, pe_rsc_promotable)) {
342 promote_demote_constraints(rsc, data_set);
343
344 /* child demoted before global demoted */
345 new_rsc_order(bundle_data->child, RSC_DEMOTED, rsc, RSC_DEMOTED,
347
348 /* global demote before child demote */
349 new_rsc_order(rsc, RSC_DEMOTE, bundle_data->child, RSC_DEMOTE,
351
352 /* child promoted before global promoted */
353 new_rsc_order(bundle_data->child, RSC_PROMOTED, rsc, RSC_PROMOTED,
355
356 /* global promote before child promote */
357 new_rsc_order(rsc, RSC_PROMOTE, bundle_data->child, RSC_PROMOTE,
359 }
360
361 } else {
362// int type = pe_order_optional | pe_order_implies_then | pe_order_restart;
363// custom_action_order(rsc, pcmk__op_key(rsc->id, RSC_STOP, 0), NULL,
364// rsc, pcmk__op_key(rsc->id, RSC_START, 0), NULL, pe_order_optional, data_set);
365 }
366}
367
368static pe_resource_t *
369compatible_replica_for_node(pe_resource_t *rsc_lh, pe_node_t *candidate,
370 pe_resource_t *rsc, enum rsc_role_e filter,
371 gboolean current)
372{
373 pe__bundle_variant_data_t *bundle_data = NULL;
374
375 CRM_CHECK(candidate != NULL, return NULL);
376 get_bundle_variant_data(bundle_data, rsc);
377
378 crm_trace("Looking for compatible child from %s for %s on %s",
379 rsc_lh->id, rsc->id, candidate->details->uname);
380
381 for (GList *gIter = bundle_data->replicas; gIter != NULL;
382 gIter = gIter->next) {
383 pe__bundle_replica_t *replica = gIter->data;
384
385 if (is_child_compatible(replica->container, candidate, filter, current)) {
386 crm_trace("Pairing %s with %s on %s",
387 rsc_lh->id, replica->container->id,
388 candidate->details->uname);
389 return replica->container;
390 }
391 }
392
393 crm_trace("Can't pair %s with %s", rsc_lh->id, rsc->id);
394 return NULL;
395}
396
397static pe_resource_t *
398compatible_replica(pe_resource_t *rsc_lh, pe_resource_t *rsc,
399 enum rsc_role_e filter, gboolean current,
400 pe_working_set_t *data_set)
401{
402 GList *scratch = NULL;
403 pe_resource_t *pair = NULL;
404 pe_node_t *active_node_lh = NULL;
405
406 active_node_lh = rsc_lh->fns->location(rsc_lh, NULL, current);
407 if (active_node_lh) {
408 return compatible_replica_for_node(rsc_lh, active_node_lh, rsc, filter,
409 current);
410 }
411
412 scratch = g_hash_table_get_values(rsc_lh->allowed_nodes);
413 scratch = sort_nodes_by_weight(scratch, NULL, data_set);
414
415 for (GList *gIter = scratch; gIter != NULL; gIter = gIter->next) {
416 pe_node_t *node = (pe_node_t *) gIter->data;
417
418 pair = compatible_replica_for_node(rsc_lh, node, rsc, filter, current);
419 if (pair) {
420 goto done;
421 }
422 }
423
424 pe_rsc_debug(rsc, "Can't pair %s with %s", rsc_lh->id, (rsc? rsc->id : "none"));
425 done:
426 g_list_free(scratch);
427 return pair;
428}
429
430void
432 pcmk__colocation_t *constraint,
433 pe_working_set_t *data_set)
434{
435 /* -- Never called --
436 *
437 * Instead we add the colocation constraints to the child and call from there
438 */
439 CRM_ASSERT(FALSE);
440}
441
443{
444 /* Strictly speaking, there should be a 'copies_per_node' addition
445 * to the resource function table and each case would be a
446 * function. However that would be serious overkill to return an
447 * int. In fact, it seems to me that both function tables
448 * could/should be replaced by resources.{c,h} full of
449 * rsc_{some_operation} functions containing a switch as below
450 * which calls out to functions named {variant}_{some_operation}
451 * as needed.
452 */
453 switch(rsc->variant) {
454 case pe_unknown:
455 return 0;
456 case pe_native:
457 case pe_group:
458 return 1;
459 case pe_clone:
460 {
461 const char *max_clones_node = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX);
462
463 if (max_clones_node == NULL) {
464 return 1;
465
466 } else {
467 int max_i;
468
469 pcmk__scan_min_int(max_clones_node, &max_i, 0);
470 return max_i;
471 }
472 }
473 case pe_container:
474 {
475 pe__bundle_variant_data_t *data = NULL;
476 get_bundle_variant_data(data, rsc);
477 return data->nreplicas_per_host;
478 }
479 }
480 return 0;
481}
482
483void
485 pcmk__colocation_t *constraint,
486 pe_working_set_t *data_set)
487{
488 GList *allocated_rhs = NULL;
489 pe__bundle_variant_data_t *bundle_data = NULL;
490
491 CRM_CHECK(constraint != NULL, return);
492 CRM_CHECK(rsc_lh != NULL, pe_err("rsc_lh was NULL for %s", constraint->id); return);
493 CRM_CHECK(rsc != NULL, pe_err("rsc was NULL for %s", constraint->id); return);
494 CRM_ASSERT(rsc_lh->variant == pe_native);
495
497 pe_rsc_trace(rsc, "%s is still provisional", rsc->id);
498 return;
499
500 } else if(constraint->rsc_lh->variant > pe_group) {
501 pe_resource_t *rh_child = compatible_replica(rsc_lh, rsc,
502 RSC_ROLE_UNKNOWN, FALSE,
503 data_set);
504
505 if (rh_child) {
506 pe_rsc_debug(rsc, "Pairing %s with %s", rsc_lh->id, rh_child->id);
507 rsc_lh->cmds->rsc_colocation_lh(rsc_lh, rh_child, constraint,
508 data_set);
509
510 } else if (constraint->score >= INFINITY) {
511 crm_notice("Cannot pair %s with instance of %s", rsc_lh->id, rsc->id);
512 assign_node(rsc_lh, NULL, TRUE);
513
514 } else {
515 pe_rsc_debug(rsc, "Cannot pair %s with instance of %s", rsc_lh->id, rsc->id);
516 }
517
518 return;
519 }
520
521 get_bundle_variant_data(bundle_data, rsc);
522 pe_rsc_trace(rsc, "Processing constraint %s: %s -> %s %d",
523 constraint->id, rsc_lh->id, rsc->id, constraint->score);
524
525 for (GList *gIter = bundle_data->replicas; gIter != NULL;
526 gIter = gIter->next) {
527 pe__bundle_replica_t *replica = gIter->data;
528
529 if (constraint->score < INFINITY) {
530 replica->container->cmds->rsc_colocation_rh(rsc_lh,
531 replica->container,
532 constraint, data_set);
533
534 } else {
535 pe_node_t *chosen = replica->container->fns->location(replica->container,
536 NULL, FALSE);
537
538 if ((chosen == NULL)
539 || is_set_recursive(replica->container, pe_rsc_block, TRUE)) {
540 continue;
541 }
542 if ((constraint->role_rh >= RSC_ROLE_PROMOTED)
543 && (replica->child == NULL)) {
544 continue;
545 }
546 if ((constraint->role_rh >= RSC_ROLE_PROMOTED)
547 && (replica->child->next_role < RSC_ROLE_PROMOTED)) {
548 continue;
549 }
550
551 pe_rsc_trace(rsc, "Allowing %s: %s %d", constraint->id, chosen->details->uname, chosen->weight);
552 allocated_rhs = g_list_prepend(allocated_rhs, chosen);
553 }
554 }
555
556 if (constraint->score >= INFINITY) {
557 node_list_exclude(rsc_lh->allowed_nodes, allocated_rhs, FALSE);
558 }
559 g_list_free(allocated_rhs);
560}
561
564{
565 GList *containers = NULL;
566 enum pe_action_flags flags = 0;
567 pe__bundle_variant_data_t *data = NULL;
568
569 get_bundle_variant_data(data, action->rsc);
570 if(data->child) {
571 enum action_tasks task = get_complex_task(data->child, action->task, TRUE);
572 switch(task) {
573 case no_action:
574 case action_notify:
575 case action_notified:
576 case action_promote:
577 case action_promoted:
578 case action_demote:
579 case action_demoted:
580 return summary_action_flags(action, data->child->children, node);
581 default:
582 break;
583 }
584 }
585
586 containers = get_container_list(action->rsc);
587 flags = summary_action_flags(action, containers, node);
588 g_list_free(containers);
589 return flags;
590}
591
594 enum rsc_role_e filter, gboolean current)
595{
596 GList *gIter = NULL;
597 GList *children = NULL;
598
599 if (local_node == NULL) {
600 crm_err("Can't colocate unrunnable child %s with %s", local_child->id, rsc->id);
601 return NULL;
602 }
603
604 crm_trace("Looking for compatible child from %s for %s on %s",
605 local_child->id, rsc->id, local_node->details->uname);
606
607 children = get_containers_or_children(rsc);
608 for (gIter = children; gIter != NULL; gIter = gIter->next) {
609 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
610
611 if(is_child_compatible(child_rsc, local_node, filter, current)) {
612 crm_trace("Pairing %s with %s on %s",
613 local_child->id, child_rsc->id, local_node->details->uname);
614 return child_rsc;
615 }
616 }
617
618 crm_trace("Can't pair %s with %s", local_child->id, rsc->id);
619 if(children != rsc->children) {
620 g_list_free(children);
621 }
622 return NULL;
623}
624
625static pe__bundle_replica_t *
626replica_for_container(pe_resource_t *rsc, pe_resource_t *container,
627 pe_node_t *node)
628{
629 if (rsc->variant == pe_container) {
630 pe__bundle_variant_data_t *data = NULL;
631
632 get_bundle_variant_data(data, rsc);
633 for (GList *gIter = data->replicas; gIter != NULL;
634 gIter = gIter->next) {
635 pe__bundle_replica_t *replica = gIter->data;
636
637 if (replica->child
638 && (container == replica->container)
639 && (node->details == replica->node->details)) {
640 return replica;
641 }
642 }
643 }
644 return NULL;
645}
646
647static enum pe_graph_flags
648multi_update_interleave_actions(pe_action_t *first, pe_action_t *then,
649 pe_node_t *node, enum pe_action_flags flags,
650 enum pe_action_flags filter,
651 enum pe_ordering type,
652 pe_working_set_t *data_set)
653{
654 GList *gIter = NULL;
655 GList *children = NULL;
656 gboolean current = FALSE;
657 enum pe_graph_flags changed = pe_graph_none;
658
659 /* Fix this - lazy */
660 if (pcmk__ends_with(first->uuid, "_stopped_0")
661 || pcmk__ends_with(first->uuid, "_demoted_0")) {
662 current = TRUE;
663 }
664
665 children = get_containers_or_children(then->rsc);
666 for (gIter = children; gIter != NULL; gIter = gIter->next) {
667 pe_resource_t *then_child = gIter->data;
668 pe_resource_t *first_child = find_compatible_child(then_child,
669 first->rsc,
671 current, data_set);
672 if (first_child == NULL && current) {
673 crm_trace("Ignore");
674
675 } else if (first_child == NULL) {
676 crm_debug("No match found for %s (%d / %s / %s)", then_child->id, current, first->uuid, then->uuid);
677
678 /* Me no like this hack - but what else can we do?
679 *
680 * If there is no-one active or about to be active
681 * on the same node as then_child, then they must
682 * not be allowed to start
683 */
684 if (type & (pe_order_runnable_left | pe_order_implies_then) /* Mandatory */ ) {
685 pe_rsc_info(then->rsc, "Inhibiting %s from being active", then_child->id);
686 if(assign_node(then_child, NULL, TRUE)) {
688 }
689 }
690
691 } else {
692 pe_action_t *first_action = NULL;
693 pe_action_t *then_action = NULL;
694
695 enum action_tasks task = clone_child_action(first);
696 const char *first_task = task2text(task);
697
698 pe__bundle_replica_t *first_replica = NULL;
699 pe__bundle_replica_t *then_replica = NULL;
700
701 first_replica = replica_for_container(first->rsc, first_child,
702 node);
703 if (strstr(first->task, "stop") && first_replica && first_replica->child) {
704 /* Except for 'stopped' we should be looking at the
705 * in-container resource, actions for the child will
706 * happen later and are therefor more likely to align
707 * with the user's intent.
708 */
709 first_action = find_first_action(first_replica->child->actions,
710 NULL, task2text(task), node);
711 } else {
712 first_action = find_first_action(first_child->actions, NULL, task2text(task), node);
713 }
714
715 then_replica = replica_for_container(then->rsc, then_child, node);
716 if (strstr(then->task, "mote")
717 && then_replica && then_replica->child) {
718 /* Promote/demote actions will never be found for the
719 * container resource, look in the child instead
720 *
721 * Alternatively treat:
722 * 'XXXX then promote YYYY' as 'XXXX then start container for YYYY', and
723 * 'demote XXXX then stop YYYY' as 'stop container for XXXX then stop YYYY'
724 */
725 then_action = find_first_action(then_replica->child->actions,
726 NULL, then->task, node);
727 } else {
728 then_action = find_first_action(then_child->actions, NULL, then->task, node);
729 }
730
731 if (first_action == NULL) {
732 if (!pcmk_is_set(first_child->flags, pe_rsc_orphan)
733 && !pcmk__str_any_of(first_task, RSC_STOP, RSC_DEMOTE, NULL)) {
734 crm_err("Internal error: No action found for %s in %s (first)",
735 first_task, first_child->id);
736
737 } else {
738 crm_trace("No action found for %s in %s%s (first)",
739 first_task, first_child->id,
740 pcmk_is_set(first_child->flags, pe_rsc_orphan)? " (ORPHAN)" : "");
741 }
742 continue;
743 }
744
745 /* We're only interested if 'then' is neither stopping nor being demoted */
746 if (then_action == NULL) {
747 if (!pcmk_is_set(then_child->flags, pe_rsc_orphan)
748 && !pcmk__str_any_of(then->task, RSC_STOP, RSC_DEMOTE, NULL)) {
749 crm_err("Internal error: No action found for %s in %s (then)",
750 then->task, then_child->id);
751
752 } else {
753 crm_trace("No action found for %s in %s%s (then)",
754 then->task, then_child->id,
755 pcmk_is_set(then_child->flags, pe_rsc_orphan)? " (ORPHAN)" : "");
756 }
757 continue;
758 }
759
760 if (order_actions(first_action, then_action, type)) {
761 crm_debug("Created constraint for %s (%d) -> %s (%d) %.6x",
762 first_action->uuid,
763 pcmk_is_set(first_action->flags, pe_action_optional),
764 then_action->uuid,
765 pcmk_is_set(then_action->flags, pe_action_optional),
766 type);
767 pe__set_graph_flags(changed, first,
769 }
770 if(first_action && then_action) {
771 changed |= then_child->cmds->update_actions(first_action,
772 then_action, node,
773 first_child->cmds->action_flags(first_action, node),
774 filter, type, data_set);
775 } else {
776 crm_err("Nothing found either for %s (%p) or %s (%p) %s",
777 first_child->id, first_action,
778 then_child->id, then_action, task2text(task));
779 }
780 }
781 }
782
783 if(children != then->rsc->children) {
784 g_list_free(children);
785 }
786 return changed;
787}
788
789static bool
790can_interleave_actions(pe_action_t *first, pe_action_t *then)
791{
792 bool interleave = FALSE;
793 pe_resource_t *rsc = NULL;
794 const char *interleave_s = NULL;
795
796 if(first->rsc == NULL || then->rsc == NULL) {
797 crm_trace("Not interleaving %s with %s (both must be resources)", first->uuid, then->uuid);
798 return FALSE;
799 } else if(first->rsc == then->rsc) {
800 crm_trace("Not interleaving %s with %s (must belong to different resources)", first->uuid, then->uuid);
801 return FALSE;
802 } else if(first->rsc->variant < pe_clone || then->rsc->variant < pe_clone) {
803 crm_trace("Not interleaving %s with %s (both sides must be clones or bundles)", first->uuid, then->uuid);
804 return FALSE;
805 }
806
807 if (pcmk__ends_with(then->uuid, "_stop_0")
808 || pcmk__ends_with(then->uuid, "_demote_0")) {
809 rsc = first->rsc;
810 } else {
811 rsc = then->rsc;
812 }
813
814 interleave_s = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INTERLEAVE);
815 interleave = crm_is_true(interleave_s);
816 crm_trace("Interleave %s -> %s: %s (based on %s)",
817 first->uuid, then->uuid, interleave ? "yes" : "no", rsc->id);
818
819 return interleave;
820}
821
824 pe_node_t *node, enum pe_action_flags flags,
825 enum pe_action_flags filter, enum pe_ordering type,
826 pe_working_set_t *data_set)
827{
828 enum pe_graph_flags changed = pe_graph_none;
829
830 crm_trace("%s -> %s", first->uuid, then->uuid);
831
832 if(can_interleave_actions(first, then)) {
833 changed = multi_update_interleave_actions(first, then, node, flags,
834 filter, type, data_set);
835
836 } else if(then->rsc) {
837 GList *gIter = NULL;
838 GList *children = NULL;
839
840 // Handle the 'primitive' ordering case
841 changed |= native_update_actions(first, then, node, flags, filter,
842 type, data_set);
843
844 // Now any children (or containers in the case of a bundle)
845 children = get_containers_or_children(then->rsc);
846 for (gIter = children; gIter != NULL; gIter = gIter->next) {
847 pe_resource_t *then_child = (pe_resource_t *) gIter->data;
848 enum pe_graph_flags then_child_changed = pe_graph_none;
849 pe_action_t *then_child_action = find_first_action(then_child->actions, NULL, then->task, node);
850
851 if (then_child_action) {
852 enum pe_action_flags then_child_flags = then_child->cmds->action_flags(then_child_action, node);
853
854 if (pcmk_is_set(then_child_flags, pe_action_runnable)) {
855 then_child_changed |= then_child->cmds->update_actions(first,
856 then_child_action, node, flags, filter, type, data_set);
857 }
858 changed |= then_child_changed;
859 if (then_child_changed & pe_graph_updated_then) {
860 for (GList *lpc = then_child_action->actions_after; lpc != NULL; lpc = lpc->next) {
861 pe_action_wrapper_t *next = (pe_action_wrapper_t *) lpc->data;
862 update_action(next->action, data_set);
863 }
864 }
865 }
866 }
867
868 if(children != then->rsc->children) {
869 g_list_free(children);
870 }
871 }
872 return changed;
873}
874
875void
877{
878 pe__bundle_variant_data_t *bundle_data = NULL;
879 get_bundle_variant_data(bundle_data, rsc);
880
881 native_rsc_location(rsc, constraint);
882
883 for (GList *gIter = bundle_data->replicas; gIter != NULL;
884 gIter = gIter->next) {
885 pe__bundle_replica_t *replica = gIter->data;
886
887 if (replica->container) {
888 replica->container->cmds->rsc_location(replica->container,
889 constraint);
890 }
891 if (replica->ip) {
892 replica->ip->cmds->rsc_location(replica->ip, constraint);
893 }
894 }
895
896 if (bundle_data->child
897 && ((constraint->role_filter == RSC_ROLE_UNPROMOTED)
898 || (constraint->role_filter == RSC_ROLE_PROMOTED))) {
899 bundle_data->child->cmds->rsc_location(bundle_data->child, constraint);
900 bundle_data->child->rsc_location = g_list_prepend(bundle_data->child->rsc_location,
901 constraint);
902 }
903}
904
905void
907{
908 pe__bundle_variant_data_t *bundle_data = NULL;
909
910 CRM_CHECK(rsc != NULL, return);
911
912 get_bundle_variant_data(bundle_data, rsc);
913
914 if (bundle_data->child) {
915 bundle_data->child->cmds->expand(bundle_data->child, data_set);
916 }
917
918 for (GList *gIter = bundle_data->replicas; gIter != NULL;
919 gIter = gIter->next) {
920 pe__bundle_replica_t *replica = gIter->data;
921
922 CRM_ASSERT(replica);
923 if (replica->remote && replica->container
924 && pe__bundle_needs_remote_name(replica->remote, data_set)) {
925
926 /* REMOTE_CONTAINER_HACK: Allow remote nodes to run containers that
927 * run pacemaker-remoted inside, without needing a separate IP for
928 * the container. This is done by configuring the inner remote's
929 * connection host as the magic string "#uname", then
930 * replacing it with the underlying host when needed.
931 */
932 xmlNode *nvpair = get_xpath_object("//nvpair[@name='" XML_RSC_ATTR_REMOTE_RA_ADDR "']",
933 replica->remote->xml, LOG_ERR);
934 const char *calculated_addr = NULL;
935
936 // Replace the value in replica->remote->xml (if appropriate)
937 calculated_addr = pe__add_bundle_remote_name(replica->remote,
938 data_set,
939 nvpair, "value");
940 if (calculated_addr) {
941 /* Since this is for the bundle as a resource, and not any
942 * particular action, replace the value in the default
943 * parameters (not evaluated for node). action2xml() will grab
944 * it from there to replace it in node-evaluated parameters.
945 */
946 GHashTable *params = pe_rsc_params(replica->remote,
947 NULL, data_set);
948
949 crm_trace("Set address for bundle connection %s to bundle host %s",
950 replica->remote->id, calculated_addr);
951 g_hash_table_replace(params,
953 strdup(calculated_addr));
954 } else {
955 /* The only way to get here is if the remote connection is
956 * neither currently running nor scheduled to run. That means we
957 * won't be doing any operations that require addr (only start
958 * requires it; we additionally use it to compare digests when
959 * unpacking status, promote, and migrate_from history, but
960 * that's already happened by this point).
961 */
962 crm_info("Unable to determine address for bundle %s remote connection",
963 rsc->id);
964 }
965 }
966 if (replica->ip) {
967 replica->ip->cmds->expand(replica->ip, data_set);
968 }
969 if (replica->container) {
970 replica->container->cmds->expand(replica->container, data_set);
971 }
972 if (replica->remote) {
973 replica->remote->cmds->expand(replica->remote, data_set);
974 }
975 }
976}
977
978gboolean
980 pe_action_t *complete, gboolean force,
981 pe_working_set_t * data_set)
982{
983 bool any_created = FALSE;
984 pe__bundle_variant_data_t *bundle_data = NULL;
985
986 CRM_CHECK(rsc != NULL, return FALSE);
987
988 get_bundle_variant_data(bundle_data, rsc);
989 for (GList *gIter = bundle_data->replicas; gIter != NULL;
990 gIter = gIter->next) {
991 pe__bundle_replica_t *replica = gIter->data;
992
993 CRM_ASSERT(replica);
994 if (replica->ip) {
995 any_created |= replica->ip->cmds->create_probe(replica->ip, node,
996 complete, force,
997 data_set);
998 }
999 if (replica->child && (node->details == replica->node->details)) {
1000 any_created |= replica->child->cmds->create_probe(replica->child,
1001 node, complete,
1002 force, data_set);
1003 }
1004 if (replica->container) {
1005 bool created = replica->container->cmds->create_probe(replica->container,
1006 node, complete,
1007 force, data_set);
1008
1009 if(created) {
1010 any_created = TRUE;
1011 /* If we're limited to one replica per host (due to
1012 * the lack of an IP range probably), then we don't
1013 * want any of our peer containers starting until
1014 * we've established that no other copies are already
1015 * running.
1016 *
1017 * Partly this is to ensure that nreplicas_per_host is
1018 * observed, but also to ensure that the containers
1019 * don't fail to start because the necessary port
1020 * mappings (which won't include an IP for uniqueness)
1021 * are already taken
1022 */
1023
1024 for (GList *tIter = bundle_data->replicas;
1025 tIter && (bundle_data->nreplicas_per_host == 1);
1026 tIter = tIter->next) {
1027 pe__bundle_replica_t *other = tIter->data;
1028
1029 if ((other != replica) && (other != NULL)
1030 && (other->container != NULL)) {
1031
1032 custom_action_order(replica->container,
1033 pcmk__op_key(replica->container->id, RSC_STATUS, 0),
1034 NULL, other->container,
1035 pcmk__op_key(other->container->id, RSC_START, 0),
1036 NULL,
1038 data_set);
1039 }
1040 }
1041 }
1042 }
1043 if (replica->container && replica->remote
1044 && replica->remote->cmds->create_probe(replica->remote, node,
1045 complete, force,
1046 data_set)) {
1047
1048 /* Do not probe the remote resource until we know where the
1049 * container is running. This is required for REMOTE_CONTAINER_HACK
1050 * to correctly probe remote resources.
1051 */
1052 char *probe_uuid = pcmk__op_key(replica->remote->id, RSC_STATUS,
1053 0);
1054 pe_action_t *probe = find_first_action(replica->remote->actions,
1055 probe_uuid, NULL, node);
1056
1057 free(probe_uuid);
1058 if (probe) {
1059 any_created = TRUE;
1060 crm_trace("Ordering %s probe on %s",
1061 replica->remote->id, node->details->uname);
1062 custom_action_order(replica->container,
1063 pcmk__op_key(replica->container->id, RSC_START, 0),
1064 NULL, replica->remote, NULL, probe,
1065 pe_order_probe, data_set);
1066 }
1067 }
1068 }
1069 return any_created;
1070}
1071
1072void
1074{
1075}
1076
1077void
1079{
1080 pe__bundle_variant_data_t *bundle_data = NULL;
1081
1082 CRM_CHECK(rsc != NULL, return);
1083
1084 get_bundle_variant_data(bundle_data, rsc);
1085 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1086 gIter = gIter->next) {
1087 pe__bundle_replica_t *replica = gIter->data;
1088
1089 CRM_ASSERT(replica);
1090 if (replica->ip) {
1091 LogActions(replica->ip, data_set);
1092 }
1093 if (replica->container) {
1094 LogActions(replica->container, data_set);
1095 }
1096 if (replica->remote) {
1097 LogActions(replica->remote, data_set);
1098 }
1099 if (replica->child) {
1100 LogActions(replica->child, data_set);
1101 }
1102 }
1103}
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition operations.c:45
uint64_t flags
Definition remote.c:3
gboolean crm_is_true(const char *s)
Definition strings.c:415
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:114
const char * task2text(enum action_tasks task)
Definition common.c:406
action_tasks
Definition common.h:62
@ no_action
Definition common.h:63
@ action_demote
Definition common.h:73
@ action_demoted
Definition common.h:74
@ action_notified
Definition common.h:70
@ action_promote
Definition common.h:71
@ action_promoted
Definition common.h:72
@ action_notify
Definition common.h:69
rsc_role_e
Possible roles that a resource can be in.
Definition common.h:92
@ RSC_ROLE_PROMOTED
Definition common.h:97
@ RSC_ROLE_UNKNOWN
Definition common.h:93
@ RSC_ROLE_UNPROMOTED
Definition common.h:96
GHashTable * pe_rsc_params(pe_resource_t *rsc, pe_node_t *node, pe_working_set_t *data_set)
Get a table of resource parameters.
Definition complex.c:457
enum crm_ais_msg_types type
Definition cpg.c:3
char data[0]
Definition cpg.c:10
uint32_t id
Definition cpg.c:0
#define RSC_PROMOTE
Definition crm.h:207
#define RSC_DEMOTE
Definition crm.h:209
#define RSC_STARTED
Definition crm.h:202
#define RSC_STOPPED
Definition crm.h:205
#define RSC_START
Definition crm.h:201
#define INFINITY
Definition crm.h:99
#define RSC_STOP
Definition crm.h:204
#define RSC_PROMOTED
Definition crm.h:208
#define RSC_STATUS
Definition crm.h:215
#define RSC_DEMOTED
Definition crm.h:210
#define crm_info(fmt, args...)
Definition logging.h:353
#define crm_warn(fmt, args...)
Definition logging.h:351
#define crm_notice(fmt, args...)
Definition logging.h:352
#define CRM_CHECK(expr, failure_action)
Definition logging.h:218
#define crm_debug(fmt, args...)
Definition logging.h:355
#define crm_err(fmt, args...)
Definition logging.h:350
#define crm_trace(fmt, args...)
Definition logging.h:356
#define XML_RSC_ATTR_INTERLEAVE
Definition msg_xml.h:224
#define XML_RSC_ATTR_REMOTE_RA_ADDR
Definition msg_xml.h:247
#define XML_RSC_ATTR_INCARNATION_NODEMAX
Definition msg_xml.h:228
const char * action
Definition pcmk_fence.c:30
enum pe_action_flags pcmk__bundle_action_flags(pe_action_t *action, pe_node_t *node)
void pcmk__bundle_rsc_colocation_rh(pe_resource_t *rsc_lh, pe_resource_t *rsc, pcmk__colocation_t *constraint, pe_working_set_t *data_set)
gboolean pcmk__bundle_create_probe(pe_resource_t *rsc, pe_node_t *node, pe_action_t *complete, gboolean force, pe_working_set_t *data_set)
void pcmk__bundle_rsc_location(pe_resource_t *rsc, pe__location_t *constraint)
void pcmk__bundle_log_actions(pe_resource_t *rsc, pe_working_set_t *data_set)
void pcmk__bundle_rsc_colocation_lh(pe_resource_t *rsc, pe_resource_t *rsc_rh, pcmk__colocation_t *constraint, pe_working_set_t *data_set)
void pcmk__bundle_append_meta(pe_resource_t *rsc, xmlNode *xml)
void pcmk__bundle_create_actions(pe_resource_t *rsc, pe_working_set_t *data_set)
pe_node_t * pcmk__bundle_allocate(pe_resource_t *rsc, pe_node_t *prefer, pe_working_set_t *data_set)
void pcmk__bundle_internal_constraints(pe_resource_t *rsc, pe_working_set_t *data_set)
void distribute_children(pe_resource_t *rsc, GList *children, GList *nodes, int max, int per_host_max, pe_working_set_t *data_set)
pe_resource_t * find_compatible_child_by_node(pe_resource_t *local_child, pe_node_t *local_node, pe_resource_t *rsc, enum rsc_role_e filter, gboolean current)
int copies_per_node(pe_resource_t *rsc)
enum pe_graph_flags pcmk__multi_update_actions(pe_action_t *first, pe_action_t *then, pe_node_t *node, enum pe_action_flags flags, enum pe_action_flags filter, enum pe_ordering type, pe_working_set_t *data_set)
void pcmk__bundle_expand(pe_resource_t *rsc, pe_working_set_t *data_set)
gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set)
void LogActions(pe_resource_t *rsc, pe_working_set_t *data_set)
void clone_create_pseudo_actions(pe_resource_t *rsc, GList *children, notify_data_t **start_notify, notify_data_t **stop_notify, pe_working_set_t *data_set)
void native_rsc_location(pe_resource_t *rsc, pe__location_t *constraint)
gboolean update_action(pe_action_t *action, pe_working_set_t *data_set)
void promote_demote_constraints(pe_resource_t *rsc, pe_working_set_t *data_set)
enum pe_graph_flags native_update_actions(pe_action_t *first, pe_action_t *then, pe_node_t *node, enum pe_action_flags flags, enum pe_action_flags filter, enum pe_ordering type, pe_working_set_t *data_set)
bool assign_node(pe_resource_t *rsc, pe_node_t *node, gboolean force)
pe_resource_t * find_compatible_child(pe_resource_t *local_child, pe_resource_t *rsc, enum rsc_role_e filter, gboolean current, pe_working_set_t *data_set)
gboolean is_child_compatible(pe_resource_t *child_rsc, pe_node_t *local_node, enum rsc_role_e filter, gboolean current)
pe_action_t * create_pseudo_resource_op(pe_resource_t *rsc, const char *task, bool optional, bool runnable, pe_working_set_t *data_set)
void pcmk__new_colocation(const char *id, const char *node_attr, int score, pe_resource_t *rsc_lh, pe_resource_t *rsc_rh, const char *state_lh, const char *state_rh, bool influence, pe_working_set_t *data_set)
enum action_tasks clone_child_action(pe_action_t *action)
GList * sort_nodes_by_weight(GList *nodes, pe_node_t *active_node, pe_working_set_t *data_set)
enum pe_action_flags summary_action_flags(pe_action_t *action, GList *children, pe_node_t *node)
#define order_start_start(rsc1, rsc2, type)
#define order_stop_stop(rsc1, rsc2, type)
int new_rsc_order(pe_resource_t *lh_rsc, const char *lh_task, pe_resource_t *rh_rsc, const char *rh_task, enum pe_ordering type, pe_working_set_t *data_set)
int custom_action_order(pe_resource_t *lh_rsc, char *lh_task, pe_action_t *lh_action, pe_resource_t *rh_rsc, char *rh_task, pe_action_t *rh_action, enum pe_ordering type, pe_working_set_t *data_set)
pe_graph_flags
Definition pe_types.h:283
@ pe_graph_updated_first
Definition pe_types.h:285
@ pe_graph_updated_then
Definition pe_types.h:286
@ pe_graph_none
Definition pe_types.h:284
#define pe_rsc_block
Definition pe_types.h:250
pe_ordering
Definition pe_types.h:484
@ pe_order_implies_first_printed
Definition pe_types.h:513
@ pe_order_implies_then
Definition pe_types.h:490
@ pe_order_same_node
Definition pe_types.h:511
@ pe_order_preserve
Definition pe_types.h:521
@ pe_order_implies_then_printed
Definition pe_types.h:514
@ pe_order_optional
Definition pe_types.h:486
@ pe_order_implies_first
Definition pe_types.h:489
@ pe_order_runnable_left
Definition pe_types.h:496
@ pe_order_probe
Definition pe_types.h:503
#define pe_rsc_provisional
Definition pe_types.h:258
#define pe_rsc_allocating
Definition pe_types.h:259
#define pe_rsc_orphan
Definition pe_types.h:248
#define pe_flag_show_scores
Definition pe_types.h:133
pe_action_flags
Definition pe_types.h:291
@ pe_action_optional
Definition pe_types.h:294
@ pe_action_runnable
Definition pe_types.h:293
@ pe_group
Definition pe_types.h:38
@ pe_container
Definition pe_types.h:40
@ pe_unknown
Definition pe_types.h:36
@ pe_native
Definition pe_types.h:37
@ pe_clone
Definition pe_types.h:39
#define pe_rsc_failure_ignored
Definition pe_types.h:275
#define pe_rsc_promotable
Definition pe_types.h:256
#define pe__show_node_weights(level, rsc, text, nodes, data_set)
Definition internal.h:353
void node_list_exclude(GHashTable *list, GList *list2, gboolean merge_scores)
Definition utils.c:161
const char * pe__add_bundle_remote_name(pe_resource_t *rsc, pe_working_set_t *data_set, xmlNode *xml, const char *field)
Definition bundle.c:968
pe_action_t * find_first_action(GList *input, const char *uuid, const char *task, pe_node_t *on_node)
Definition utils.c:1428
bool is_set_recursive(pe_resource_t *rsc, long long flag, bool any)
Definition clone.c:397
#define pe__set_graph_flags(graph_flags, gr_action, flags_to_set)
Definition internal.h:125
int pe_get_failcount(pe_node_t *node, pe_resource_t *rsc, time_t *last_failure, uint32_t flags, xmlNode *xml_op, pe_working_set_t *data_set)
Definition failcounts.c:251
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition internal.h:53
#define pe_rsc_debug(rsc, fmt, args...)
Definition internal.h:19
bool pe__bundle_needs_remote_name(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition bundle.c:950
enum action_tasks get_complex_task(pe_resource_t *rsc, const char *name, gboolean allow_non_atomic)
Definition utils.c:1405
gboolean order_actions(pe_action_t *lh_action, pe_action_t *rh_action, enum pe_ordering order)
Definition utils.c:1808
#define pe_rsc_trace(rsc, fmt, args...)
Definition internal.h:20
#define pe__set_resource_flags(resource, flags_to_set)
Definition internal.h:47
#define pe_rsc_info(rsc, fmt, args...)
Definition internal.h:18
@ pe_fc_effective
Definition internal.h:300
@ pe_fc_fillers
Definition internal.h:301
#define pe_err(fmt...)
Definition internal.h:22
bool pe__is_guest_or_remote_node(const pe_node_t *node)
Definition remote.c:41
#define CRM_ASSERT(expr)
Definition results.h:42
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:127
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:535
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:979
pe_resource_t * rsc_lh
enum rsc_role_e role_filter
Definition internal.h:171
pe_resource_t * rsc
Definition pe_types.h:411
char * uuid
Definition pe_types.h:416
char * task
Definition pe_types.h:415
GList * actions_after
Definition pe_types.h:449
enum pe_action_flags flags
Definition pe_types.h:420
pe_action_t * action
Definition pe_types.h:535
int weight
Definition pe_types.h:241
struct pe_node_shared_s * details
Definition pe_types.h:244
const char * uname
Definition pe_types.h:209
pe_resource_t * remote_rsc
Definition pe_types.h:230
GList * actions
Definition pe_types.h:360
enum pe_obj_types variant
Definition pe_types.h:331
GHashTable * meta
Definition pe_types.h:374
GList * children
Definition pe_types.h:378
int migration_threshold
Definition pe_types.h:345
pe_resource_t * container
Definition pe_types.h:381
GHashTable * allowed_nodes
Definition pe_types.h:369
unsigned long long flags
Definition pe_types.h:349
resource_alloc_functions_t * cmds
Definition pe_types.h:334
resource_object_functions_t * fns
Definition pe_types.h:333
unsigned long long flags
Definition pe_types.h:146
void(* rsc_colocation_lh)(pe_resource_t *, pe_resource_t *, pcmk__colocation_t *, pe_working_set_t *)
enum pe_action_flags(* action_flags)(pe_action_t *, pe_node_t *)
enum pe_graph_flags(* update_actions)(pe_action_t *, pe_action_t *, pe_node_t *, enum pe_action_flags, enum pe_action_flags, enum pe_ordering, pe_working_set_t *data_set)
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition pe_types.h:54
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition xpath.c:214