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