pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
bundle.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2025 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 Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11
12#include <ctype.h>
13#include <stdbool.h> // bool, true, false
14#include <stdint.h>
15
16#include <crm/pengine/status.h>
18#include <crm/common/xml.h>
19#include <crm/common/output.h>
21#include <pe_status_private.h>
22
25
26 // mount instance-specific subdirectory rather than source directly
28};
29
30typedef struct {
31 char *source;
32 char *target;
33 char *options;
34 uint32_t flags; // bitmask of pe__bundle_mount_flags
35} pe__bundle_mount_t;
36
37typedef struct {
38 char *source;
39 char *target;
40} pe__bundle_port_t;
41
47
48#define PE__CONTAINER_AGENT_UNKNOWN_S "unknown"
49#define PE__CONTAINER_AGENT_DOCKER_S "docker"
50#define PE__CONTAINER_AGENT_PODMAN_S "podman"
51
52typedef struct pe__bundle_variant_data_s {
53 int promoted_max;
54 int nreplicas;
55 int nreplicas_per_host;
56 char *prefix;
57 char *image;
58 const char *ip_last;
59 char *host_network;
60 char *host_netmask;
61 char *control_port;
62 char *container_network;
63 char *ip_range_start;
64 gboolean add_host;
65 gchar *container_host_options;
66 char *container_command;
67 char *launcher_options;
68 const char *attribute_target;
69
70 pcmk_resource_t *child;
71
72 GList *replicas; // pcmk__bundle_replica_t *
73 GList *ports; // pe__bundle_port_t *
74 GList *mounts; // pe__bundle_mount_t *
75
76 /* @TODO Maybe use a more object-oriented design instead, with a set of
77 * methods that are different per type rather than switching on this
78 */
79 enum pe__container_agent agent_type;
81
82#define get_bundle_variant_data(data, rsc) do { \
83 pcmk__assert(pcmk__is_bundle(rsc)); \
84 data = rsc->priv->variant_opaque; \
85 } while (0)
86
95int
97{
98 const pe__bundle_variant_data_t *bundle_data = NULL;
99
100 get_bundle_variant_data(bundle_data, pe__const_top_resource(rsc, true));
101 return bundle_data->nreplicas;
102}
103
114{
115 const pe__bundle_variant_data_t *bundle_data = NULL;
116
117 get_bundle_variant_data(bundle_data, pe__const_top_resource(rsc, true));
118 return bundle_data->child;
119}
120
130const pcmk_resource_t *
132{
133 const pe__bundle_variant_data_t *data = NULL;
134 const pcmk_resource_t *top = pe__const_top_resource(instance, true);
135
136 if (!pcmk__is_bundle(top)) {
137 return NULL;
138 }
140
141 for (const GList *iter = data->replicas; iter != NULL; iter = iter->next) {
142 const pcmk__bundle_replica_t *replica = iter->data;
143
144 if (instance == replica->container) {
145 return replica->child;
146 }
147 }
148 return NULL;
149}
150
160bool
162 const pcmk_node_t *node)
163{
164 pe__bundle_variant_data_t *bundle_data = NULL;
165
166 get_bundle_variant_data(bundle_data, bundle);
167 for (GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
168 pcmk__bundle_replica_t *replica = iter->data;
169
170 if (pcmk__same_node(node, replica->node)) {
171 return true;
172 }
173 }
174 return false;
175}
176
188{
189 const pe__bundle_variant_data_t *bundle_data = NULL;
190 const pcmk__bundle_replica_t *replica = NULL;
191
192 get_bundle_variant_data(bundle_data, bundle);
193 if (bundle_data->replicas == NULL) {
194 return NULL;
195 }
196 replica = bundle_data->replicas->data;
197 return replica->container;
198}
199
209void
211 bool (*fn)(pcmk__bundle_replica_t *, void *),
212 void *user_data)
213{
214 const pe__bundle_variant_data_t *bundle_data = NULL;
215
216 get_bundle_variant_data(bundle_data, bundle);
217 for (GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
218 if (!fn((pcmk__bundle_replica_t *) iter->data, user_data)) {
219 break;
220 }
221 }
222}
223
233void
235 bool (*fn)(const pcmk__bundle_replica_t *,
236 void *),
237 void *user_data)
238{
239 const pe__bundle_variant_data_t *bundle_data = NULL;
240
241 get_bundle_variant_data(bundle_data, bundle);
242 for (const GList *iter = bundle_data->replicas; iter != NULL;
243 iter = iter->next) {
244
245 if (!fn((const pcmk__bundle_replica_t *) iter->data, user_data)) {
246 break;
247 }
248 }
249}
250
251static char *
252next_ip(const char *last_ip)
253{
254 unsigned int oct1 = 0;
255 unsigned int oct2 = 0;
256 unsigned int oct3 = 0;
257 unsigned int oct4 = 0;
258 int rc = sscanf(last_ip, "%u.%u.%u.%u", &oct1, &oct2, &oct3, &oct4);
259
260 if (rc != 4) {
261 /*@ TODO check for IPv6 */
262 return NULL;
263
264 } else if (oct3 > 253) {
265 return NULL;
266
267 } else if (oct4 > 253) {
268 ++oct3;
269 oct4 = 1;
270
271 } else {
272 ++oct4;
273 }
274
275 return crm_strdup_printf("%u.%u.%u.%u", oct1, oct2, oct3, oct4);
276}
277
278static void
280 GString *buffer)
281{
282 if(data->ip_range_start == NULL) {
283 return;
284
285 } else if(data->ip_last) {
286 replica->ipaddr = next_ip(data->ip_last);
287
288 } else {
289 replica->ipaddr = strdup(data->ip_range_start);
290 }
291
292 data->ip_last = replica->ipaddr;
293 switch (data->agent_type) {
296 if (data->add_host) {
297 g_string_append_printf(buffer, " --add-host=%s-%d:%s",
298 data->prefix, replica->offset,
299 replica->ipaddr);
300 } else {
301 g_string_append_printf(buffer, " --hosts-entry=%s=%s-%d",
302 replica->ipaddr, data->prefix,
303 replica->offset);
304 }
305 break;
306
307 default: // PE__CONTAINER_AGENT_UNKNOWN
308 break;
309 }
310}
311
312static xmlNode *
313create_resource(const char *name, const char *provider, const char *kind)
314{
315 xmlNode *rsc = pcmk__xe_create(NULL, PCMK_XE_PRIMITIVE);
316
319 crm_xml_add(rsc, PCMK_XA_PROVIDER, provider);
320 crm_xml_add(rsc, PCMK_XA_TYPE, kind);
321
322 return rsc;
323}
324
337static bool
338valid_network(pe__bundle_variant_data_t *data)
339{
340 if(data->ip_range_start) {
341 return TRUE;
342 }
343 if(data->control_port) {
344 if(data->nreplicas_per_host > 1) {
345 pcmk__config_err("Specifying the '" PCMK_XA_CONTROL_PORT "' for %s "
346 "requires '" PCMK_XA_REPLICAS_PER_HOST "=1'",
347 data->prefix);
348 data->nreplicas_per_host = 1;
349 // @TODO to be sure:
350 // pcmk__clear_rsc_flags(rsc, pcmk__rsc_unique);
351 }
352 return TRUE;
353 }
354 return FALSE;
355}
356
357static int
359 pcmk__bundle_replica_t *replica)
360{
361 if(data->ip_range_start) {
362 char *id = NULL;
363 xmlNode *xml_ip = NULL;
364 xmlNode *xml_obj = NULL;
365
366 id = crm_strdup_printf("%s-ip-%s", data->prefix, replica->ipaddr);
368 xml_ip = create_resource(id, "heartbeat", "IPaddr2");
369 free(id);
370
372 pcmk__xe_set_id(xml_obj, "%s-attributes-%d",
373 data->prefix, replica->offset);
374
375 crm_create_nvpair_xml(xml_obj, NULL, "ip", replica->ipaddr);
376 if(data->host_network) {
377 crm_create_nvpair_xml(xml_obj, NULL, "nic", data->host_network);
378 }
379
380 if(data->host_netmask) {
381 crm_create_nvpair_xml(xml_obj, NULL,
382 "cidr_netmask", data->host_netmask);
383
384 } else {
385 crm_create_nvpair_xml(xml_obj, NULL, "cidr_netmask", "32");
386 }
387
388 xml_obj = pcmk__xe_create(xml_ip, PCMK_XE_OPERATIONS);
389 crm_create_op_xml(xml_obj, pcmk__xe_id(xml_ip), PCMK_ACTION_MONITOR,
390 "60s", NULL);
391
392 // TODO: Other ops? Timeouts and intervals from underlying resource?
393
394 if (pe__unpack_resource(xml_ip, &replica->ip, parent,
397 }
398
399 parent->priv->children = g_list_append(parent->priv->children,
400 replica->ip);
401 }
402 return pcmk_rc_ok;
403}
404
405static const char*
406container_agent_str(enum pe__container_agent t)
407{
408 switch (t) {
411 default: // PE__CONTAINER_AGENT_UNKNOWN
412 break;
413 }
415}
416
417static int
418create_container_resource(pcmk_resource_t *parent,
420 pcmk__bundle_replica_t *replica)
421{
422 char *id = NULL;
423 xmlNode *xml_container = NULL;
424 xmlNode *xml_obj = NULL;
425
426 // Agent-specific
427 const char *hostname_opt = NULL;
428 const char *env_opt = NULL;
429 const char *agent_str = NULL;
430
431 GString *buffer = NULL;
432 GString *dbuffer = NULL;
433
434 // Where syntax differences are drop-in replacements, set them now
435 switch (data->agent_type) {
438 hostname_opt = "-h ";
439 env_opt = "-e ";
440 break;
441 default: // PE__CONTAINER_AGENT_UNKNOWN
443 }
444 agent_str = container_agent_str(data->agent_type);
445
446 buffer = g_string_sized_new(4096);
447
448 id = crm_strdup_printf("%s-%s-%d", data->prefix, agent_str,
449 replica->offset);
451 xml_container = create_resource(id, "heartbeat", agent_str);
452 free(id);
453
454 xml_obj = pcmk__xe_create(xml_container, PCMK_XE_INSTANCE_ATTRIBUTES);
455 pcmk__xe_set_id(xml_obj, "%s-attributes-%d", data->prefix, replica->offset);
456
457 crm_create_nvpair_xml(xml_obj, NULL, "image", data->image);
458 crm_create_nvpair_xml(xml_obj, NULL, "allow_pull", PCMK_VALUE_TRUE);
459 crm_create_nvpair_xml(xml_obj, NULL, "force_kill", PCMK_VALUE_FALSE);
460 crm_create_nvpair_xml(xml_obj, NULL, "reuse", PCMK_VALUE_FALSE);
461
462 if (data->agent_type == PE__CONTAINER_AGENT_DOCKER) {
463 g_string_append(buffer, " --restart=no");
464 }
465
466 /* Set a container hostname only if we have an IP to map it to. The user can
467 * set -h or --uts=host themselves if they want a nicer name for logs, but
468 * this makes applications happy who need their hostname to match the IP
469 * they bind to.
470 */
471 if (data->ip_range_start != NULL) {
472 g_string_append_printf(buffer, " %s%s-%d", hostname_opt, data->prefix,
473 replica->offset);
474 }
475 pcmk__g_strcat(buffer, " ", env_opt, "PCMK_stderr=1", NULL);
476
477 if (data->container_network != NULL) {
478 pcmk__g_strcat(buffer, " --net=", data->container_network, NULL);
479 }
480
481 if (data->control_port != NULL) {
482 pcmk__g_strcat(buffer, " ", env_opt, "PCMK_" PCMK__ENV_REMOTE_PORT "=",
483 data->control_port, NULL);
484 } else {
485 g_string_append_printf(buffer, " %sPCMK_" PCMK__ENV_REMOTE_PORT "=%d",
486 env_opt, DEFAULT_REMOTE_PORT);
487 }
488
489 for (GList *iter = data->mounts; iter != NULL; iter = iter->next) {
490 pe__bundle_mount_t *mount = (pe__bundle_mount_t *) iter->data;
491 char *source = NULL;
492
493 if (pcmk_is_set(mount->flags, pe__bundle_mount_subdir)) {
494 source = crm_strdup_printf("%s/%s-%d", mount->source, data->prefix,
495 replica->offset);
496 pcmk__add_separated_word(&dbuffer, 1024, source, ",");
497 }
498
499 switch (data->agent_type) {
502 pcmk__g_strcat(buffer,
503 " -v ", pcmk__s(source, mount->source),
504 ":", mount->target, NULL);
505
506 if (mount->options != NULL) {
507 pcmk__g_strcat(buffer, ":", mount->options, NULL);
508 }
509 break;
510 default:
511 break;
512 }
513 free(source);
514 }
515
516 for (GList *iter = data->ports; iter != NULL; iter = iter->next) {
517 pe__bundle_port_t *port = (pe__bundle_port_t *) iter->data;
518
519 switch (data->agent_type) {
522 if (replica->ipaddr != NULL) {
523 pcmk__g_strcat(buffer,
524 " -p ", replica->ipaddr, ":", port->source,
525 ":", port->target, NULL);
526
527 } else if (!pcmk__str_eq(data->container_network,
529 // No need to do port mapping if net == host
530 pcmk__g_strcat(buffer,
531 " -p ", port->source, ":", port->target,
532 NULL);
533 }
534 break;
535 default:
536 break;
537 }
538 }
539
540 /* @COMPAT: We should use pcmk__add_word() here, but we can't yet, because
541 * it would cause restarts during rolling upgrades.
542 *
543 * In a previous version of the container resource creation logic, if
544 * data->launcher_options is not NULL, we append
545 * (" %s", data->launcher_options) even if data->launcher_options is an
546 * empty string. Likewise for data->container_host_options. Using
547 *
548 * pcmk__add_word(buffer, 0, data->launcher_options)
549 *
550 * removes that extra trailing space, causing a resource definition change.
551 */
552 if (data->launcher_options != NULL) {
553 pcmk__g_strcat(buffer, " ", data->launcher_options, NULL);
554 }
555
556 if (data->container_host_options != NULL) {
557 pcmk__g_strcat(buffer, " ", data->container_host_options, NULL);
558 }
559
560 crm_create_nvpair_xml(xml_obj, NULL, "run_opts",
561 (const char *) buffer->str);
562 g_string_free(buffer, TRUE);
563
564 crm_create_nvpair_xml(xml_obj, NULL, "mount_points",
565 (dbuffer != NULL)? (const char *) dbuffer->str : "");
566 if (dbuffer != NULL) {
567 g_string_free(dbuffer, TRUE);
568 }
569
570 if (replica->child != NULL) {
571 if (data->container_command != NULL) {
572 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
573 data->container_command);
574 } else {
575 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
577 }
578
579 /* TODO: Allow users to specify their own?
580 *
581 * We just want to know if the container is alive; we'll monitor the
582 * child independently.
583 */
584 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true");
585#if 0
586 /* @TODO Consider supporting the use case where we can start and stop
587 * resources, but not proxy local commands (such as setting node
588 * attributes), by running the local executor in stand-alone mode.
589 * However, this would probably be better done via ACLs as with other
590 * Pacemaker Remote nodes.
591 */
592 } else if ((child != NULL) && data->untrusted) {
593 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
595 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd",
596 CRM_DAEMON_DIR "/pacemaker/cts-exec-helper -c poke");
597#endif
598 } else {
599 if (data->container_command != NULL) {
600 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
601 data->container_command);
602 }
603
604 /* TODO: Allow users to specify their own?
605 *
606 * We don't know what's in the container, so we just want to know if it
607 * is alive.
608 */
609 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true");
610 }
611
612 xml_obj = pcmk__xe_create(xml_container, PCMK_XE_OPERATIONS);
613 crm_create_op_xml(xml_obj, pcmk__xe_id(xml_container), PCMK_ACTION_MONITOR,
614 "60s", NULL);
615
616 // TODO: Other ops? Timeouts and intervals from underlying resource?
617 if (pe__unpack_resource(xml_container, &replica->container, parent,
620 }
622 parent->priv->children = g_list_append(parent->priv->children,
623 replica->container);
624
625 return pcmk_rc_ok;
626}
627
634static void
635disallow_node(pcmk_resource_t *rsc, const char *uname)
636{
637 gpointer match = g_hash_table_lookup(rsc->priv->allowed_nodes, uname);
638
639 if (match) {
640 ((pcmk_node_t *) match)->assign->score = -PCMK_SCORE_INFINITY;
641 ((pcmk_node_t *) match)->assign->probe_mode = pcmk__probe_never;
642 }
643 g_list_foreach(rsc->priv->children, (GFunc) disallow_node,
644 (gpointer) uname);
645}
646
647static int
648create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data,
649 pcmk__bundle_replica_t *replica)
650{
651 if (replica->child && valid_network(data)) {
652 GHashTableIter gIter;
653 pcmk_node_t *node = NULL;
654 xmlNode *xml_remote = NULL;
655 char *id = crm_strdup_printf("%s-%d", data->prefix, replica->offset);
656 char *port_s = NULL;
657 const char *uname = NULL;
658 const char *connect_name = NULL;
659 pcmk_scheduler_t *scheduler = parent->priv->scheduler;
660
661 if (pe_find_resource(scheduler->priv->resources, id) != NULL) {
662 free(id);
663 // The biggest hammer we have
664 id = crm_strdup_printf("pcmk-internal-%s-remote-%d",
665 replica->child->id, replica->offset);
666 //@TODO return error instead of asserting?
668 id) == NULL);
669 }
670
671 /* REMOTE_CONTAINER_HACK: Using "#uname" as the server name when the
672 * connection does not have its own IP is a magic string that we use to
673 * support nested remotes (i.e. a bundle running on a remote node).
674 */
675 connect_name = (replica->ipaddr? replica->ipaddr : "#uname");
676
677 if (data->control_port == NULL) {
678 port_s = pcmk__itoa(DEFAULT_REMOTE_PORT);
679 }
680
681 /* This sets replica->container as replica->remote's container, which is
682 * similar to what happens with guest nodes. This is how the scheduler
683 * knows that the bundle node is fenced by recovering the container, and
684 * that remote should be ordered relative to the container.
685 */
686 xml_remote = pe_create_remote_xml(NULL, id, replica->container->id,
687 NULL, NULL, NULL,
688 connect_name, (data->control_port?
689 data->control_port : port_s));
690 free(port_s);
691
692 /* Abandon our created ID, and pull the copy from the XML, because we
693 * need something that will get freed during scheduler data cleanup to
694 * use as the node ID and uname.
695 */
696 free(id);
697 id = NULL;
698 uname = pcmk__xe_id(xml_remote);
699
700 /* Ensure a node has been created for the guest (it may have already
701 * been, if it has a permanent node attribute), and ensure its weight is
702 * -INFINITY so no other resources can run on it.
703 */
705 if (node == NULL) {
708 } else {
710 }
712
713 /* unpack_remote_nodes() ensures that each remote node and guest node
714 * has a pcmk_node_t entry. Ideally, it would do the same for bundle
715 * nodes. Unfortunately, a bundle has to be mostly unpacked before it's
716 * obvious what nodes will be needed, so we do it just above.
717 *
718 * Worse, that means that the node may have been utilized while
719 * unpacking other resources, without our weight correction. The most
720 * likely place for this to happen is when pe__unpack_resource() calls
721 * resource_location() to set a default score in symmetric clusters.
722 * This adds a node *copy* to each resource's allowed nodes, and these
723 * copies will have the wrong weight.
724 *
725 * As a hacky workaround, fix those copies here.
726 *
727 * @TODO Possible alternative: ensure bundles are unpacked before other
728 * resources, so the weight is correct before any copies are made.
729 */
730 g_list_foreach(scheduler->priv->resources,
731 (GFunc) disallow_node, (gpointer) uname);
732
733 replica->node = pe__copy_node(node);
734 replica->node->assign->score = 500;
736
737 /* Ensure the node shows up as allowed and with the correct discovery set */
738 if (replica->child->priv->allowed_nodes != NULL) {
739 g_hash_table_destroy(replica->child->priv->allowed_nodes);
740 }
741 replica->child->priv->allowed_nodes =
743 g_hash_table_insert(replica->child->priv->allowed_nodes,
744 (gpointer) replica->node->priv->id,
745 pe__copy_node(replica->node));
746
747 {
748 const pcmk_resource_t *parent = replica->child->priv->parent;
749 pcmk_node_t *copy = pe__copy_node(replica->node);
750
752 g_hash_table_insert(parent->priv->allowed_nodes,
753 (gpointer) replica->node->priv->id, copy);
754 }
755 if (pe__unpack_resource(xml_remote, &replica->remote, parent,
756 scheduler) != pcmk_rc_ok) {
758 }
759
760 g_hash_table_iter_init(&gIter, replica->remote->priv->allowed_nodes);
761 while (g_hash_table_iter_next(&gIter, NULL, (void **)&node)) {
762 if (pcmk__is_pacemaker_remote_node(node)) {
763 /* Remote resources can only run on 'normal' cluster node */
764 node->assign->score = -PCMK_SCORE_INFINITY;
765 }
766 }
767
768 replica->node->priv->remote = replica->remote;
769
770 // Ensure pcmk__is_guest_or_bundle_node() functions correctly
771 replica->remote->priv->launcher = replica->container;
772
773 /* A bundle's #kind is closer to "container" (guest node) than the
774 * "remote" set by pe_create_node().
775 */
776 pcmk__insert_dup(replica->node->priv->attrs,
777 CRM_ATTR_KIND, "container");
778
779 /* One effect of this is that unpack_launcher() will add
780 * replica->remote to replica->container's launched resources, which
781 * will make pe__resource_contains_guest_node() true for
782 * replica->container.
783 *
784 * replica->child does NOT get added to replica->container's launched
785 * resources. The only noticeable effect if it did would be for its
786 * fail count to be taken into account when checking
787 * replica->container's migration threshold.
788 */
789 parent->priv->children = g_list_append(parent->priv->children,
790 replica->remote);
791 }
792 return pcmk_rc_ok;
793}
794
795static int
796create_replica_resources(pcmk_resource_t *parent,
798 pcmk__bundle_replica_t *replica)
799{
800 int rc = pcmk_rc_ok;
801
802 rc = create_container_resource(parent, data, replica);
803 if (rc != pcmk_rc_ok) {
804 return rc;
805 }
806
807 rc = create_ip_resource(parent, data, replica);
808 if (rc != pcmk_rc_ok) {
809 return rc;
810 }
811
812 rc = create_remote_resource(parent, data, replica);
813 if (rc != pcmk_rc_ok) {
814 return rc;
815 }
816
817 if ((replica->child != NULL) && (replica->ipaddr != NULL)) {
818 pcmk__insert_meta(replica->child->priv, "external-ip", replica->ipaddr);
819 }
820
821 if (replica->remote != NULL) {
822 /*
823 * Allow the remote connection resource to be allocated to a
824 * different node than the one on which the container is active.
825 *
826 * This makes it possible to have Pacemaker Remote nodes running
827 * containers with the remote executor inside in order to start
828 * services inside those containers.
829 */
831 }
832 return rc;
833}
834
835static void
836mount_add(pe__bundle_variant_data_t *bundle_data, const char *source,
837 const char *target, const char *options, uint32_t flags)
838{
839 pe__bundle_mount_t *mount = pcmk__assert_alloc(1,
840 sizeof(pe__bundle_mount_t));
841
842 mount->source = pcmk__str_copy(source);
843 mount->target = pcmk__str_copy(target);
844 mount->options = pcmk__str_copy(options);
845 mount->flags = flags;
846 bundle_data->mounts = g_list_append(bundle_data->mounts, mount);
847}
848
849static void
850mount_free(pe__bundle_mount_t *mount)
851{
852 free(mount->source);
853 free(mount->target);
854 free(mount->options);
855 free(mount);
856}
857
858static void
859port_free(pe__bundle_port_t *port)
860{
861 free(port->source);
862 free(port->target);
863 free(port);
864}
865
867replica_for_remote(pcmk_resource_t *remote)
868{
869 pcmk_resource_t *top = remote;
870 pe__bundle_variant_data_t *bundle_data = NULL;
871
872 if (top == NULL) {
873 return NULL;
874 }
875 while (top->priv->parent != NULL) {
876 top = top->priv->parent;
877 }
878
879 get_bundle_variant_data(bundle_data, top);
880 for (GList *gIter = bundle_data->replicas; gIter != NULL;
881 gIter = gIter->next) {
882 pcmk__bundle_replica_t *replica = gIter->data;
883
884 if (replica->remote == remote) {
885 return replica;
886 }
887 }
888 CRM_LOG_ASSERT(FALSE);
889 return NULL;
890}
891
892bool
894{
895 const char *value;
896 GHashTable *params = NULL;
897
898 if (rsc == NULL) {
899 return false;
900 }
901
902 // Use NULL node since pcmk__bundle_expand() uses that to set value
903 params = pe_rsc_params(rsc, NULL, rsc->priv->scheduler);
904 value = g_hash_table_lookup(params, PCMK_REMOTE_RA_ADDR);
905
906 return pcmk__str_eq(value, "#uname", pcmk__str_casei)
908}
909
910const char *
912 const char *field)
913{
914 // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
915
916 pcmk_node_t *node = NULL;
917 pcmk__bundle_replica_t *replica = NULL;
918
920 return NULL;
921 }
922
923 replica = replica_for_remote(rsc);
924 if (replica == NULL) {
925 return NULL;
926 }
927
928 node = replica->container->priv->assigned_node;
929 if (node == NULL) {
930 /* If it won't be running anywhere after the
931 * transition, go with where it's running now.
932 */
933 node = pcmk__current_node(replica->container);
934 }
935
936 if(node == NULL) {
937 crm_trace("Cannot determine address for bundle connection %s", rsc->id);
938 return NULL;
939 }
940
941 crm_trace("Setting address for bundle connection %s to bundle host %s",
942 rsc->id, pcmk__node_name(node));
943 if(xml != NULL && field != NULL) {
944 crm_xml_add(xml, field, node->priv->name);
945 }
946
947 return node->priv->name;
948}
949
950#define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set) do { \
951 flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \
952 "Bundle mount", pcmk__xe_id(mount_xml), \
953 flags, (flags_to_set), #flags_to_set); \
954 } while (0)
955
956bool
958{
959 const char *value = NULL;
960 xmlNode *xml_obj = NULL;
961 const xmlNode *xml_child = NULL;
962 xmlNode *xml_resource = NULL;
963 pe__bundle_variant_data_t *bundle_data = NULL;
964 bool need_log_mount = TRUE;
965
966 pcmk__assert(rsc != NULL);
967 pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
968
969 bundle_data = pcmk__assert_alloc(1, sizeof(pe__bundle_variant_data_t));
970 rsc->priv->variant_opaque = bundle_data;
971 bundle_data->prefix = strdup(rsc->id);
972
973 xml_obj = pcmk__xe_first_child(rsc->priv->xml, PCMK_XE_DOCKER, NULL,
974 NULL);
975 if (xml_obj != NULL) {
976 bundle_data->agent_type = PE__CONTAINER_AGENT_DOCKER;
977 }
978
979 if (xml_obj == NULL) {
980 xml_obj = pcmk__xe_first_child(rsc->priv->xml, PCMK_XE_PODMAN, NULL,
981 NULL);
982 if (xml_obj != NULL) {
983 bundle_data->agent_type = PE__CONTAINER_AGENT_PODMAN;
984 }
985 }
986
987 if (xml_obj == NULL) {
988 return FALSE;
989 }
990
991 // Use 0 for default, minimum, and invalid PCMK_XA_PROMOTED_MAX
992 value = crm_element_value(xml_obj, PCMK_XA_PROMOTED_MAX);
993 pcmk__scan_min_int(value, &bundle_data->promoted_max, 0);
994
995 /* Default replicas to PCMK_XA_PROMOTED_MAX if it was specified and 1
996 * otherwise
997 */
998 value = crm_element_value(xml_obj, PCMK_XA_REPLICAS);
999 if ((value == NULL) && (bundle_data->promoted_max > 0)) {
1000 bundle_data->nreplicas = bundle_data->promoted_max;
1001 } else {
1002 pcmk__scan_min_int(value, &bundle_data->nreplicas, 1);
1003 }
1004
1005 /*
1006 * Communication between containers on the same host via the
1007 * floating IPs only works if the container is started with:
1008 * --userland-proxy=false --ip-masq=false
1009 */
1011 pcmk__scan_min_int(value, &bundle_data->nreplicas_per_host, 1);
1012 if (bundle_data->nreplicas_per_host == 1) {
1014 }
1015
1016 bundle_data->container_command =
1018 bundle_data->launcher_options = crm_element_value_copy(xml_obj,
1020 bundle_data->image = crm_element_value_copy(xml_obj, PCMK_XA_IMAGE);
1021 bundle_data->container_network = crm_element_value_copy(xml_obj,
1023
1024 xml_obj = pcmk__xe_first_child(rsc->priv->xml, PCMK_XE_NETWORK, NULL,
1025 NULL);
1026 if(xml_obj) {
1027 bundle_data->ip_range_start =
1029 bundle_data->host_netmask =
1031 bundle_data->host_network =
1033 bundle_data->control_port =
1035 value = crm_element_value(xml_obj, PCMK_XA_ADD_HOST);
1036 if (crm_str_to_boolean(value, &bundle_data->add_host) != 1) {
1037 bundle_data->add_host = TRUE;
1038 }
1039
1040 for (xml_child = pcmk__xe_first_child(xml_obj, PCMK_XE_PORT_MAPPING,
1041 NULL, NULL);
1042 xml_child != NULL;
1043 xml_child = pcmk__xe_next(xml_child, PCMK_XE_PORT_MAPPING)) {
1044
1045 pe__bundle_port_t *port =
1046 pcmk__assert_alloc(1, sizeof(pe__bundle_port_t));
1047
1048 port->source = crm_element_value_copy(xml_child, PCMK_XA_PORT);
1049
1050 if(port->source == NULL) {
1051 port->source = crm_element_value_copy(xml_child, PCMK_XA_RANGE);
1052 } else {
1053 port->target = crm_element_value_copy(xml_child,
1055 }
1056
1057 if(port->source != NULL && strlen(port->source) > 0) {
1058 if(port->target == NULL) {
1059 port->target = strdup(port->source);
1060 }
1061 bundle_data->ports = g_list_append(bundle_data->ports, port);
1062
1063 } else {
1064 pcmk__config_err("Invalid " PCMK_XA_PORT " directive %s",
1065 pcmk__xe_id(xml_child));
1066 port_free(port);
1067 }
1068 }
1069 }
1070
1071 xml_obj = pcmk__xe_first_child(rsc->priv->xml, PCMK_XE_STORAGE, NULL,
1072 NULL);
1073 for (xml_child = pcmk__xe_first_child(xml_obj, PCMK_XE_STORAGE_MAPPING,
1074 NULL, NULL);
1075 xml_child != NULL;
1076 xml_child = pcmk__xe_next(xml_child, PCMK_XE_STORAGE_MAPPING)) {
1077
1078 const char *source = crm_element_value(xml_child, PCMK_XA_SOURCE_DIR);
1079 const char *target = crm_element_value(xml_child, PCMK_XA_TARGET_DIR);
1080 const char *options = crm_element_value(xml_child, PCMK_XA_OPTIONS);
1082
1083 if (source == NULL) {
1084 source = crm_element_value(xml_child, PCMK_XA_SOURCE_DIR_ROOT);
1087 }
1088
1089 if (source && target) {
1090 mount_add(bundle_data, source, target, options, flags);
1091 if (strcmp(target, "/var/log") == 0) {
1092 need_log_mount = FALSE;
1093 }
1094 } else {
1095 pcmk__config_err("Invalid mount directive %s",
1096 pcmk__xe_id(xml_child));
1097 }
1098 }
1099
1100 xml_obj = pcmk__xe_first_child(rsc->priv->xml, PCMK_XE_PRIMITIVE, NULL,
1101 NULL);
1102 if (xml_obj && valid_network(bundle_data)) {
1103 const char *suffix = NULL;
1104 char *value = NULL;
1105 xmlNode *xml_set = NULL;
1106
1107 xml_resource = pcmk__xe_create(NULL, PCMK_XE_CLONE);
1108
1109 /* @COMPAT We no longer use the <master> tag, but we need to keep it as
1110 * part of the resource name, so that bundles don't restart in a rolling
1111 * upgrade. (It also avoids needing to change regression tests.)
1112 */
1113 suffix = (const char *) xml_resource->name;
1114 if (bundle_data->promoted_max > 0) {
1115 suffix = "master";
1116 }
1117
1118 pcmk__xe_set_id(xml_resource, "%s-%s", bundle_data->prefix, suffix);
1119
1120 xml_set = pcmk__xe_create(xml_resource, PCMK_XE_META_ATTRIBUTES);
1121 pcmk__xe_set_id(xml_set, "%s-%s-meta",
1122 bundle_data->prefix, xml_resource->name);
1123
1124 crm_create_nvpair_xml(xml_set, NULL,
1126
1127 value = pcmk__itoa(bundle_data->nreplicas);
1128 crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_MAX, value);
1129 free(value);
1130
1131 value = pcmk__itoa(bundle_data->nreplicas_per_host);
1132 crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_NODE_MAX, value);
1133 free(value);
1134
1136 pcmk__btoa(bundle_data->nreplicas_per_host > 1));
1137
1138 if (bundle_data->promoted_max) {
1139 crm_create_nvpair_xml(xml_set, NULL,
1141
1142 value = pcmk__itoa(bundle_data->promoted_max);
1143 crm_create_nvpair_xml(xml_set, NULL, PCMK_META_PROMOTED_MAX, value);
1144 free(value);
1145 }
1146
1147 //crm_xml_add(xml_obj, PCMK_XA_ID, bundle_data->prefix);
1148 pcmk__xml_copy(xml_resource, xml_obj);
1149
1150 } else if(xml_obj) {
1151 pcmk__config_err("Cannot control %s inside %s without either "
1153 rsc->id, pcmk__xe_id(xml_obj));
1154 return FALSE;
1155 }
1156
1157 if(xml_resource) {
1158 int lpc = 0;
1159 GList *childIter = NULL;
1160 pe__bundle_port_t *port = NULL;
1161 GString *buffer = NULL;
1162
1163 if (pe__unpack_resource(xml_resource, &(bundle_data->child), rsc,
1164 rsc->priv->scheduler) != pcmk_rc_ok) {
1165 return FALSE;
1166 }
1167
1168 /* Currently, we always map the default authentication key location
1169 * into the same location inside the container.
1170 *
1171 * Ideally, we would respect the host's PCMK_authkey_location, but:
1172 * - it may be different on different nodes;
1173 * - the actual connection will do extra checking to make sure the key
1174 * file exists and is readable, that we can't do here on the DC
1175 * - tools such as crm_resource and crm_simulate may not have the same
1176 * environment variables as the cluster, causing operation digests to
1177 * differ
1178 *
1179 * Always using the default location inside the container is fine,
1180 * because we control the pacemaker_remote environment, and it avoids
1181 * having to pass another environment variable to the container.
1182 *
1183 * @TODO A better solution may be to have only pacemaker_remote use the
1184 * environment variable, and have the cluster nodes use a new
1185 * cluster option for key location. This would introduce the limitation
1186 * of the location being the same on all cluster nodes, but that's
1187 * reasonable.
1188 */
1189 mount_add(bundle_data, DEFAULT_REMOTE_KEY_LOCATION,
1191
1192 if (need_log_mount) {
1193 mount_add(bundle_data, CRM_BUNDLE_DIR, "/var/log", NULL,
1195 }
1196
1197 port = pcmk__assert_alloc(1, sizeof(pe__bundle_port_t));
1198 if(bundle_data->control_port) {
1199 port->source = strdup(bundle_data->control_port);
1200 } else {
1201 /* If we wanted to respect PCMK_remote_port, we could use
1202 * crm_default_remote_port() here and elsewhere in this file instead
1203 * of DEFAULT_REMOTE_PORT.
1204 *
1205 * However, it gains nothing, since we control both the container
1206 * environment and the connection resource parameters, and the user
1207 * can use a different port if desired by setting
1208 * PCMK_XA_CONTROL_PORT.
1209 */
1210 port->source = pcmk__itoa(DEFAULT_REMOTE_PORT);
1211 }
1212 port->target = strdup(port->source);
1213 bundle_data->ports = g_list_append(bundle_data->ports, port);
1214
1215 buffer = g_string_sized_new(1024);
1216 for (childIter = bundle_data->child->priv->children;
1217 childIter != NULL; childIter = childIter->next) {
1218
1219 pcmk__bundle_replica_t *replica = NULL;
1220
1221 replica = pcmk__assert_alloc(1, sizeof(pcmk__bundle_replica_t));
1222 replica->child = childIter->data;
1224 replica->offset = lpc++;
1225
1226 // Ensure the child's notify gets set based on the underlying primitive's value
1227 if (pcmk_is_set(replica->child->flags, pcmk__rsc_notify)) {
1228 pcmk__set_rsc_flags(bundle_data->child, pcmk__rsc_notify);
1229 }
1230
1231 allocate_ip(bundle_data, replica, buffer);
1232 bundle_data->replicas = g_list_append(bundle_data->replicas,
1233 replica);
1234 // coverity[null_field] replica->child can't be NULL here
1235 bundle_data->attribute_target =
1236 g_hash_table_lookup(replica->child->priv->meta,
1238 }
1239 bundle_data->container_host_options = g_string_free(buffer, FALSE);
1240
1241 if (bundle_data->attribute_target) {
1244 bundle_data->attribute_target);
1245 pcmk__insert_dup(bundle_data->child->priv->meta,
1247 bundle_data->attribute_target);
1248 }
1249
1250 } else {
1251 // Just a naked container, no pacemaker-remote
1252 GString *buffer = g_string_sized_new(1024);
1253
1254 for (int lpc = 0; lpc < bundle_data->nreplicas; lpc++) {
1255 pcmk__bundle_replica_t *replica = NULL;
1256
1257 replica = pcmk__assert_alloc(1, sizeof(pcmk__bundle_replica_t));
1258 replica->offset = lpc;
1259 allocate_ip(bundle_data, replica, buffer);
1260 bundle_data->replicas = g_list_append(bundle_data->replicas,
1261 replica);
1262 }
1263 bundle_data->container_host_options = g_string_free(buffer, FALSE);
1264 }
1265
1266 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1267 gIter = gIter->next) {
1268 pcmk__bundle_replica_t *replica = gIter->data;
1269
1270 if (create_replica_resources(rsc, bundle_data, replica) != pcmk_rc_ok) {
1271 pcmk__config_err("Failed unpacking resource %s", rsc->id);
1273 return FALSE;
1274 }
1275
1276 /* Utilization needs special handling for bundles. It makes no sense for
1277 * the inner primitive to have utilization, because it is tied
1278 * one-to-one to the guest node created by the container resource -- and
1279 * there's no way to set capacities for that guest node anyway.
1280 *
1281 * What the user really wants is to configure utilization for the
1282 * container. However, the schema only allows utilization for
1283 * primitives, and the container resource is implicit anyway, so the
1284 * user can *only* configure utilization for the inner primitive. If
1285 * they do, move the primitive's utilization values to the container.
1286 *
1287 * @TODO This means that bundles without an inner primitive can't have
1288 * utilization. An alternative might be to allow utilization values in
1289 * the top-level bundle XML in the schema, and copy those to each
1290 * container.
1291 */
1292 if (replica->child != NULL) {
1293 GHashTable *empty = replica->container->priv->utilization;
1294
1295 replica->container->priv->utilization =
1296 replica->child->priv->utilization;
1297
1298 replica->child->priv->utilization = empty;
1299 }
1300 }
1301
1302 if (bundle_data->child) {
1303 rsc->priv->children = g_list_append(rsc->priv->children,
1304 bundle_data->child);
1305 }
1306 return TRUE;
1307}
1308
1309static int
1310replica_resource_active(pcmk_resource_t *rsc, gboolean all)
1311{
1312 if (rsc) {
1313 gboolean child_active = rsc->priv->fns->active(rsc, all);
1314
1315 if (child_active && !all) {
1316 return TRUE;
1317 } else if (!child_active && all) {
1318 return FALSE;
1319 }
1320 }
1321 return -1;
1322}
1323
1324bool
1326{
1327 pe__bundle_variant_data_t *bundle_data = NULL;
1328 GList *iter = NULL;
1329
1330 get_bundle_variant_data(bundle_data, rsc);
1331 for (iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
1332 pcmk__bundle_replica_t *replica = iter->data;
1333 int rsc_active;
1334
1335 rsc_active = replica_resource_active(replica->ip, all);
1336 if (rsc_active >= 0) {
1337 return (bool) rsc_active;
1338 }
1339
1340 rsc_active = replica_resource_active(replica->child, all);
1341 if (rsc_active >= 0) {
1342 return (bool) rsc_active;
1343 }
1344
1345 rsc_active = replica_resource_active(replica->container, all);
1346 if (rsc_active >= 0) {
1347 return (bool) rsc_active;
1348 }
1349
1350 rsc_active = replica_resource_active(replica->remote, all);
1351 if (rsc_active >= 0) {
1352 return (bool) rsc_active;
1353 }
1354 }
1355
1356 /* If "all" is TRUE, we've already checked that no resources were inactive,
1357 * so return TRUE; if "all" is FALSE, we didn't find any active resources,
1358 * so return FALSE.
1359 */
1360 return all;
1361}
1362
1374{
1375 pe__bundle_variant_data_t *bundle_data = NULL;
1376
1377 pcmk__assert((bundle != NULL) && (node != NULL));
1378
1379 get_bundle_variant_data(bundle_data, bundle);
1380 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1381 gIter = gIter->next) {
1382 pcmk__bundle_replica_t *replica = gIter->data;
1383
1384 pcmk__assert((replica != NULL) && (replica->node != NULL));
1385 if (pcmk__same_node(replica->node, node)) {
1386 return replica->child;
1387 }
1388 }
1389 return NULL;
1390}
1391
1392PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1393 "GList *")
1394int
1395pe__bundle_xml(pcmk__output_t *out, va_list args)
1396{
1397 uint32_t show_opts = va_arg(args, uint32_t);
1398 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1399 GList *only_node = va_arg(args, GList *);
1400 GList *only_rsc = va_arg(args, GList *);
1401
1402 pe__bundle_variant_data_t *bundle_data = NULL;
1403 int rc = pcmk_rc_no_output;
1404 gboolean printed_header = FALSE;
1405 bool print_everything = true;
1406
1407 const char *desc = NULL;
1408
1409 pcmk__assert(rsc != NULL);
1410 get_bundle_variant_data(bundle_data, rsc);
1411
1412 if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
1413 return rc;
1414 }
1415
1416 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1417
1418 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1419 gIter = gIter->next) {
1420 pcmk__bundle_replica_t *replica = gIter->data;
1421 pcmk_resource_t *ip = replica->ip;
1422 pcmk_resource_t *child = replica->child;
1423 pcmk_resource_t *container = replica->container;
1424 pcmk_resource_t *remote = replica->remote;
1425 char *id = NULL;
1426 gboolean print_ip, print_child, print_ctnr, print_remote;
1427
1428 pcmk__assert(replica != NULL);
1429
1430 if (pcmk__rsc_filtered_by_node(container, only_node)) {
1431 continue;
1432 }
1433
1434 print_ip = (ip != NULL)
1435 && !ip->priv->fns->is_filtered(ip, only_rsc,
1436 print_everything);
1437 print_child = (child != NULL)
1438 && !child->priv->fns->is_filtered(child, only_rsc,
1439 print_everything);
1440 print_ctnr = !container->priv->fns->is_filtered(container, only_rsc,
1441 print_everything);
1442 print_remote = (remote != NULL)
1443 && !remote->priv->fns->is_filtered(remote, only_rsc,
1444 print_everything);
1445
1446 if (!print_everything && !print_ip && !print_child && !print_ctnr && !print_remote) {
1447 continue;
1448 }
1449
1450 if (!printed_header) {
1451 const char *type = container_agent_str(bundle_data->agent_type);
1452 const char *unique = pcmk__flag_text(rsc->flags, pcmk__rsc_unique);
1453 const char *maintenance = pcmk__flag_text(rsc->flags,
1455 const char *managed = pcmk__flag_text(rsc->flags,
1457 const char *failed = pcmk__flag_text(rsc->flags, pcmk__rsc_failed);
1458
1459 printed_header = TRUE;
1460
1461 desc = pe__resource_description(rsc, show_opts);
1462
1464 PCMK_XA_ID, rsc->id,
1466 PCMK_XA_IMAGE, bundle_data->image,
1467 PCMK_XA_UNIQUE, unique,
1468 PCMK_XA_MAINTENANCE, maintenance,
1469 PCMK_XA_MANAGED, managed,
1470 PCMK_XA_FAILED, failed,
1471 PCMK_XA_DESCRIPTION, desc,
1472 NULL);
1473 pcmk__assert(rc == pcmk_rc_ok);
1474 }
1475
1476 id = pcmk__itoa(replica->offset);
1478 PCMK_XA_ID, id,
1479 NULL);
1480 free(id);
1481 pcmk__assert(rc == pcmk_rc_ok);
1482
1483 if (print_ip) {
1484 out->message(out, (const char *) ip->priv->xml->name, show_opts,
1485 ip, only_node, only_rsc);
1486 }
1487
1488 if (print_child) {
1489 out->message(out, (const char *) child->priv->xml->name,
1490 show_opts, child, only_node, only_rsc);
1491 }
1492
1493 if (print_ctnr) {
1494 out->message(out, (const char *) container->priv->xml->name,
1495 show_opts, container, only_node, only_rsc);
1496 }
1497
1498 if (print_remote) {
1499 out->message(out, (const char *) remote->priv->xml->name,
1500 show_opts, remote, only_node, only_rsc);
1501 }
1502
1503 pcmk__output_xml_pop_parent(out); // replica
1504 }
1505
1506 if (printed_header) {
1507 pcmk__output_xml_pop_parent(out); // bundle
1508 }
1509
1510 return rc;
1511}
1512
1513static void
1514pe__bundle_replica_output_html(pcmk__output_t *out,
1515 pcmk__bundle_replica_t *replica,
1516 pcmk_node_t *node, uint32_t show_opts)
1517{
1518 pcmk_resource_t *rsc = replica->child;
1519
1520 int offset = 0;
1521 char buffer[LINE_MAX];
1522
1523 if(rsc == NULL) {
1524 rsc = replica->container;
1525 }
1526
1527 if (replica->remote) {
1528 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1529 rsc_printable_id(replica->remote));
1530 } else {
1531 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1532 rsc_printable_id(replica->container));
1533 }
1534 if (replica->ipaddr) {
1535 offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1536 replica->ipaddr);
1537 }
1538
1539 pe__common_output_html(out, rsc, buffer, node, show_opts);
1540}
1541
1551static const char *
1552get_unmanaged_str(const pcmk_resource_t *rsc)
1553{
1555 return " (maintenance)";
1556 }
1557 if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
1558 return " (unmanaged)";
1559 }
1560 return "";
1561}
1562
1563PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1564 "GList *")
1565int
1566pe__bundle_html(pcmk__output_t *out, va_list args)
1567{
1568 uint32_t show_opts = va_arg(args, uint32_t);
1569 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1570 GList *only_node = va_arg(args, GList *);
1571 GList *only_rsc = va_arg(args, GList *);
1572
1573 const char *desc = NULL;
1574 pe__bundle_variant_data_t *bundle_data = NULL;
1575 int rc = pcmk_rc_no_output;
1576 bool print_everything = true;
1577
1578 pcmk__assert(rsc != NULL);
1579 get_bundle_variant_data(bundle_data, rsc);
1580
1581 desc = pe__resource_description(rsc, show_opts);
1582
1583 if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
1584 return rc;
1585 }
1586
1587 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1588
1589 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1590 gIter = gIter->next) {
1591 pcmk__bundle_replica_t *replica = gIter->data;
1592 pcmk_resource_t *ip = replica->ip;
1593 pcmk_resource_t *child = replica->child;
1594 pcmk_resource_t *container = replica->container;
1595 pcmk_resource_t *remote = replica->remote;
1596 gboolean print_ip, print_child, print_ctnr, print_remote;
1597
1598 pcmk__assert(replica != NULL);
1599
1600 if (pcmk__rsc_filtered_by_node(container, only_node)) {
1601 continue;
1602 }
1603
1604 print_ip = (ip != NULL)
1605 && !ip->priv->fns->is_filtered(ip, only_rsc,
1606 print_everything);
1607 print_child = (child != NULL)
1608 && !child->priv->fns->is_filtered(child, only_rsc,
1609 print_everything);
1610 print_ctnr = !container->priv->fns->is_filtered(container, only_rsc,
1611 print_everything);
1612 print_remote = (remote != NULL)
1613 && !remote->priv->fns->is_filtered(remote, only_rsc,
1614 print_everything);
1615
1616 if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
1617 (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
1618 /* The text output messages used below require pe_print_implicit to
1619 * be set to do anything.
1620 */
1621 uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
1622
1623 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1624 (bundle_data->nreplicas > 1)? " set" : "",
1625 rsc->id, bundle_data->image,
1626 pcmk_is_set(rsc->flags, pcmk__rsc_unique)? " (unique)" : "",
1627 desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1628 get_unmanaged_str(rsc));
1629
1630 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1631 out->begin_list(out, NULL, NULL, "Replica[%d]", replica->offset);
1632 }
1633
1634 if (print_ip) {
1635 out->message(out, (const char *) ip->priv->xml->name,
1636 new_show_opts, ip, only_node, only_rsc);
1637 }
1638
1639 if (print_child) {
1640 out->message(out, (const char *) child->priv->xml->name,
1641 new_show_opts, child, only_node, only_rsc);
1642 }
1643
1644 if (print_ctnr) {
1645 out->message(out, (const char *) container->priv->xml->name,
1646 new_show_opts, container, only_node, only_rsc);
1647 }
1648
1649 if (print_remote) {
1650 out->message(out, (const char *) remote->priv->xml->name,
1651 new_show_opts, remote, only_node, only_rsc);
1652 }
1653
1654 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1655 out->end_list(out);
1656 }
1657 } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
1658 continue;
1659 } else {
1660 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1661 (bundle_data->nreplicas > 1)? " set" : "",
1662 rsc->id, bundle_data->image,
1663 pcmk_is_set(rsc->flags, pcmk__rsc_unique)? " (unique)" : "",
1664 desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1665 get_unmanaged_str(rsc));
1666
1667 pe__bundle_replica_output_html(out, replica,
1668 pcmk__current_node(container),
1669 show_opts);
1670 }
1671 }
1672
1673 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1674 return rc;
1675}
1676
1677static void
1678pe__bundle_replica_output_text(pcmk__output_t *out,
1679 pcmk__bundle_replica_t *replica,
1680 pcmk_node_t *node, uint32_t show_opts)
1681{
1682 const pcmk_resource_t *rsc = replica->child;
1683
1684 int offset = 0;
1685 char buffer[LINE_MAX];
1686
1687 if(rsc == NULL) {
1688 rsc = replica->container;
1689 }
1690
1691 if (replica->remote) {
1692 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1693 rsc_printable_id(replica->remote));
1694 } else {
1695 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1696 rsc_printable_id(replica->container));
1697 }
1698 if (replica->ipaddr) {
1699 offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1700 replica->ipaddr);
1701 }
1702
1703 pe__common_output_text(out, rsc, buffer, node, show_opts);
1704}
1705
1706PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1707 "GList *")
1708int
1709pe__bundle_text(pcmk__output_t *out, va_list args)
1710{
1711 uint32_t show_opts = va_arg(args, uint32_t);
1712 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1713 GList *only_node = va_arg(args, GList *);
1714 GList *only_rsc = va_arg(args, GList *);
1715
1716 const char *desc = NULL;
1717 pe__bundle_variant_data_t *bundle_data = NULL;
1718 int rc = pcmk_rc_no_output;
1719 bool print_everything = true;
1720
1721 desc = pe__resource_description(rsc, show_opts);
1722
1723 pcmk__assert(rsc != NULL);
1724 get_bundle_variant_data(bundle_data, rsc);
1725
1726 if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
1727 return rc;
1728 }
1729
1730 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1731
1732 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1733 gIter = gIter->next) {
1734 pcmk__bundle_replica_t *replica = gIter->data;
1735 pcmk_resource_t *ip = replica->ip;
1736 pcmk_resource_t *child = replica->child;
1737 pcmk_resource_t *container = replica->container;
1738 pcmk_resource_t *remote = replica->remote;
1739 gboolean print_ip, print_child, print_ctnr, print_remote;
1740
1741 pcmk__assert(replica != NULL);
1742
1743 if (pcmk__rsc_filtered_by_node(container, only_node)) {
1744 continue;
1745 }
1746
1747 print_ip = (ip != NULL)
1748 && !ip->priv->fns->is_filtered(ip, only_rsc,
1749 print_everything);
1750 print_child = (child != NULL)
1751 && !child->priv->fns->is_filtered(child, only_rsc,
1752 print_everything);
1753 print_ctnr = !container->priv->fns->is_filtered(container, only_rsc,
1754 print_everything);
1755 print_remote = (remote != NULL)
1756 && !remote->priv->fns->is_filtered(remote, only_rsc,
1757 print_everything);
1758
1759 if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
1760 (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
1761 /* The text output messages used below require pe_print_implicit to
1762 * be set to do anything.
1763 */
1764 uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
1765
1766 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1767 (bundle_data->nreplicas > 1)? " set" : "",
1768 rsc->id, bundle_data->image,
1769 pcmk_is_set(rsc->flags, pcmk__rsc_unique)? " (unique)" : "",
1770 desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1771 get_unmanaged_str(rsc));
1772
1773 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1774 out->list_item(out, NULL, "Replica[%d]", replica->offset);
1775 }
1776
1777 out->begin_list(out, NULL, NULL, NULL);
1778
1779 if (print_ip) {
1780 out->message(out, (const char *) ip->priv->xml->name,
1781 new_show_opts, ip, only_node, only_rsc);
1782 }
1783
1784 if (print_child) {
1785 out->message(out, (const char *) child->priv->xml->name,
1786 new_show_opts, child, only_node, only_rsc);
1787 }
1788
1789 if (print_ctnr) {
1790 out->message(out, (const char *) container->priv->xml->name,
1791 new_show_opts, container, only_node, only_rsc);
1792 }
1793
1794 if (print_remote) {
1795 out->message(out, (const char *) remote->priv->xml->name,
1796 new_show_opts, remote, only_node, only_rsc);
1797 }
1798
1799 out->end_list(out);
1800 } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
1801 continue;
1802 } else {
1803 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1804 (bundle_data->nreplicas > 1)? " set" : "",
1805 rsc->id, bundle_data->image,
1806 pcmk_is_set(rsc->flags, pcmk__rsc_unique)? " (unique)" : "",
1807 desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1808 get_unmanaged_str(rsc));
1809
1810 pe__bundle_replica_output_text(out, replica,
1811 pcmk__current_node(container),
1812 show_opts);
1813 }
1814 }
1815
1816 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1817 return rc;
1818}
1819
1820static void
1821free_bundle_replica(pcmk__bundle_replica_t *replica)
1822{
1823 if (replica == NULL) {
1824 return;
1825 }
1826
1827 pcmk__free_node_copy(replica->node);
1828 replica->node = NULL;
1829
1830 if (replica->ip) {
1831 pcmk__xml_free(replica->ip->priv->xml);
1832 replica->ip->priv->xml = NULL;
1833 pcmk__free_resource(replica->ip);
1834 }
1835 if (replica->container) {
1836 pcmk__xml_free(replica->container->priv->xml);
1837 replica->container->priv->xml = NULL;
1839 }
1840 if (replica->remote) {
1841 pcmk__xml_free(replica->remote->priv->xml);
1842 replica->remote->priv->xml = NULL;
1843 pcmk__free_resource(replica->remote);
1844 }
1845 free(replica->ipaddr);
1846 free(replica);
1847}
1848
1849void
1851{
1852 pe__bundle_variant_data_t *bundle_data = NULL;
1853 CRM_CHECK(rsc != NULL, return);
1854
1855 get_bundle_variant_data(bundle_data, rsc);
1856 pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
1857
1858 free(bundle_data->prefix);
1859 free(bundle_data->image);
1860 free(bundle_data->control_port);
1861 free(bundle_data->host_network);
1862 free(bundle_data->host_netmask);
1863 free(bundle_data->ip_range_start);
1864 free(bundle_data->container_network);
1865 free(bundle_data->launcher_options);
1866 free(bundle_data->container_command);
1867 g_free(bundle_data->container_host_options);
1868
1869 g_list_free_full(bundle_data->replicas,
1870 (GDestroyNotify) free_bundle_replica);
1871 g_list_free_full(bundle_data->mounts, (GDestroyNotify)mount_free);
1872 g_list_free_full(bundle_data->ports, (GDestroyNotify)port_free);
1873 g_list_free(rsc->priv->children);
1874
1875 if(bundle_data->child) {
1876 pcmk__xml_free(bundle_data->child->priv->xml);
1877 bundle_data->child->priv->xml = NULL;
1878 pcmk__free_resource(bundle_data->child);
1879 }
1880 common_free(rsc);
1881}
1882
1883enum rsc_role_e
1885{
1886 enum rsc_role_e container_role = pcmk_role_unknown;
1887 return container_role;
1888}
1889
1897int
1899{
1900 if (pcmk__is_bundle(rsc)) {
1901 pe__bundle_variant_data_t *bundle_data = NULL;
1902
1903 get_bundle_variant_data(bundle_data, rsc);
1904 return bundle_data->nreplicas;
1905 }
1906 return 0;
1907}
1908
1909void
1911{
1912 pe__bundle_variant_data_t *bundle_data = NULL;
1913
1914 get_bundle_variant_data(bundle_data, rsc);
1915 for (GList *item = bundle_data->replicas; item != NULL; item = item->next) {
1916 pcmk__bundle_replica_t *replica = item->data;
1917
1918 if (replica->ip) {
1919 replica->ip->priv->fns->count(replica->ip);
1920 }
1921 if (replica->child) {
1922 replica->child->priv->fns->count(replica->child);
1923 }
1924 if (replica->container) {
1925 replica->container->priv->fns->count(replica->container);
1926 }
1927 if (replica->remote) {
1928 replica->remote->priv->fns->count(replica->remote);
1929 }
1930 }
1931}
1932
1933bool
1934pe__bundle_is_filtered(const pcmk_resource_t *rsc, const GList *only_rsc,
1935 bool check_parent)
1936{
1937 bool passes = false;
1938 pe__bundle_variant_data_t *bundle_data = NULL;
1939
1941 passes = true;
1942 } else {
1943 get_bundle_variant_data(bundle_data, rsc);
1944
1945 for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) {
1946 pcmk__bundle_replica_t *replica = gIter->data;
1947 pcmk_resource_t *ip = replica->ip;
1948 pcmk_resource_t *child = replica->child;
1949 pcmk_resource_t *container = replica->container;
1950 pcmk_resource_t *remote = replica->remote;
1951
1952 if ((ip != NULL)
1953 && !ip->priv->fns->is_filtered(ip, only_rsc, false)) {
1954 passes = true;
1955 break;
1956 }
1957 if ((child != NULL)
1958 && !child->priv->fns->is_filtered(child, only_rsc, false)) {
1959 passes = true;
1960 break;
1961 }
1962 if (!container->priv->fns->is_filtered(container, only_rsc,
1963 false)) {
1964 passes = true;
1965 break;
1966 }
1967 if ((remote != NULL)
1968 && !remote->priv->fns->is_filtered(remote, only_rsc, false)) {
1969 passes = true;
1970 break;
1971 }
1972 }
1973 }
1974
1975 return !passes;
1976}
1977
1988GList *
1990{
1991 /* @TODO It would be more efficient to do this once when unpacking the
1992 * bundle, creating a new GList* in the variant data
1993 */
1994 GList *containers = NULL;
1995 const pe__bundle_variant_data_t *data = NULL;
1996
1998 for (GList *iter = data->replicas; iter != NULL; iter = iter->next) {
1999 pcmk__bundle_replica_t *replica = iter->data;
2000
2001 containers = g_list_append(containers, replica->container);
2002 }
2003 return containers;
2004}
2005
2006// Bundle implementation of pcmk__rsc_methods_t:active_node()
2008pe__bundle_active_node(const pcmk_resource_t *rsc, unsigned int *count_all,
2009 unsigned int *count_clean)
2010{
2011 pcmk_node_t *active = NULL;
2012 pcmk_node_t *node = NULL;
2013 pcmk_resource_t *container = NULL;
2014 GList *containers = NULL;
2015 GList *iter = NULL;
2016 GHashTable *nodes = NULL;
2017 const pe__bundle_variant_data_t *data = NULL;
2018
2019 if (count_all != NULL) {
2020 *count_all = 0;
2021 }
2022 if (count_clean != NULL) {
2023 *count_clean = 0;
2024 }
2025 if (rsc == NULL) {
2026 return NULL;
2027 }
2028
2029 /* For the purposes of this method, we only care about where the bundle's
2030 * containers are active, so build a list of active containers.
2031 */
2033 for (iter = data->replicas; iter != NULL; iter = iter->next) {
2034 pcmk__bundle_replica_t *replica = iter->data;
2035
2036 if (replica->container->priv->active_nodes != NULL) {
2037 containers = g_list_append(containers, replica->container);
2038 }
2039 }
2040 if (containers == NULL) {
2041 return NULL;
2042 }
2043
2044 /* If the bundle has only a single active container, just use that
2045 * container's method. If live migration is ever supported for bundle
2046 * containers, this will allow us to prefer the migration source when there
2047 * is only one container and it is migrating. For now, this just lets us
2048 * avoid creating the nodes table.
2049 */
2050 if (pcmk__list_of_1(containers)) {
2051 container = containers->data;
2052 node = container->priv->fns->active_node(container, count_all,
2053 count_clean);
2054 g_list_free(containers);
2055 return node;
2056 }
2057
2058 // Add all containers' active nodes to a hash table (for uniqueness)
2059 nodes = g_hash_table_new(NULL, NULL);
2060 for (iter = containers; iter != NULL; iter = iter->next) {
2061 container = iter->data;
2062 for (GList *node_iter = container->priv->active_nodes;
2063 node_iter != NULL; node_iter = node_iter->next) {
2064
2065 node = node_iter->data;
2066
2067 // If insert returns true, we haven't counted this node yet
2068 if (g_hash_table_insert(nodes, (gpointer) node->details,
2069 (gpointer) node)
2070 && !pe__count_active_node(rsc, node, &active, count_all,
2071 count_clean)) {
2072 goto done;
2073 }
2074 }
2075 }
2076
2077done:
2078 g_list_free(containers);
2079 g_hash_table_destroy(nodes);
2080 return active;
2081}
2082
2091unsigned int
2093{
2094 pe__bundle_variant_data_t *bundle_data = NULL;
2095
2096 get_bundle_variant_data(bundle_data, rsc);
2097 pcmk__assert(bundle_data->nreplicas_per_host >= 0);
2098 return (unsigned int) bundle_data->nreplicas_per_host;
2099}
xmlNode * crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task, const char *interval_spec, const char *timeout)
Create a CIB XML element for an operation.
Definition actions.c:549
#define PCMK_ACTION_MONITOR
Definition actions.h:51
#define PCMK_RESOURCE_CLASS_OCF
Definition agents.h:27
const char * pe__add_bundle_remote_name(pcmk_resource_t *rsc, xmlNode *xml, const char *field)
Definition bundle.c:911
void pe__foreach_const_bundle_replica(const pcmk_resource_t *bundle, bool(*fn)(const pcmk__bundle_replica_t *, void *), void *user_data)
Definition bundle.c:234
enum rsc_role_e pe__bundle_resource_state(const pcmk_resource_t *rsc, bool current)
Definition bundle.c:1884
pe__container_agent
Definition bundle.c:42
@ PE__CONTAINER_AGENT_PODMAN
Definition bundle.c:45
@ PE__CONTAINER_AGENT_UNKNOWN
Definition bundle.c:43
@ PE__CONTAINER_AGENT_DOCKER
Definition bundle.c:44
bool pe__bundle_is_filtered(const pcmk_resource_t *rsc, const GList *only_rsc, bool check_parent)
Definition bundle.c:1934
struct pe__bundle_variant_data_s pe__bundle_variant_data_t
void pe__count_bundle(pcmk_resource_t *rsc)
Definition bundle.c:1910
#define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set)
Definition bundle.c:950
pcmk_resource_t * pe__find_bundle_replica(const pcmk_resource_t *bundle, const pcmk_node_t *node)
Definition bundle.c:1373
const pcmk_resource_t * pe__get_rsc_in_container(const pcmk_resource_t *instance)
Definition bundle.c:131
int pe_bundle_replicas(const pcmk_resource_t *rsc)
Get the number of configured replicas in a bundle.
Definition bundle.c:1898
#define PE__CONTAINER_AGENT_UNKNOWN_S
Definition bundle.c:48
unsigned int pe__bundle_max_per_node(const pcmk_resource_t *rsc)
Definition bundle.c:2092
pcmk_node_t * pe__bundle_active_node(const pcmk_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean)
Definition bundle.c:2008
pcmk_resource_t * pe__first_container(const pcmk_resource_t *bundle)
Definition bundle.c:187
bool pe__bundle_needs_remote_name(pcmk_resource_t *rsc)
Definition bundle.c:893
#define PE__CONTAINER_AGENT_PODMAN_S
Definition bundle.c:50
pe__bundle_mount_flags
Definition bundle.c:23
@ pe__bundle_mount_subdir
Definition bundle.c:27
@ pe__bundle_mount_none
Definition bundle.c:24
GList * pe__bundle_containers(const pcmk_resource_t *bundle)
Definition bundle.c:1989
pcmk_resource_t * pe__bundled_resource(const pcmk_resource_t *rsc)
Definition bundle.c:113
bool pe__unpack_bundle(pcmk_resource_t *rsc)
Definition bundle.c:957
#define get_bundle_variant_data(data, rsc)
Definition bundle.c:82
#define PE__CONTAINER_AGENT_DOCKER_S
Definition bundle.c:49
bool pe__node_is_bundle_instance(const pcmk_resource_t *bundle, const pcmk_node_t *node)
Definition bundle.c:161
bool pe__bundle_active(const pcmk_resource_t *rsc, bool all)
Definition bundle.c:1325
void pe__foreach_bundle_replica(pcmk_resource_t *bundle, bool(*fn)(pcmk__bundle_replica_t *, void *), void *user_data)
Definition bundle.c:210
void pe__free_bundle(pcmk_resource_t *rsc)
Definition bundle.c:1850
int pe__bundle_max(const pcmk_resource_t *rsc)
Definition bundle.c:96
const char * parent
Definition cib.c:27
const char * name
Definition cib.c:26
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
uint64_t flags
Definition remote.c:3
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
int pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, pcmk_resource_t *parent, pcmk_scheduler_t *scheduler)
Definition complex.c:686
GHashTable * pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Get a table of resource parameters.
Definition complex.c:462
#define SBIN_DIR
Definition config.h:526
#define CRM_BUNDLE_DIR
Definition config.h:11
#define CRM_DAEMON_DIR
Definition config.h:21
char uname[MAX_NAME]
Definition cpg.c:5
char data[0]
Definition cpg.c:10
enum pcmk_ipc_server type
Definition cpg.c:3
#define CRM_ATTR_KIND
Definition crm.h:94
#define CRM_LOG_ASSERT(expr)
Definition logging.h:196
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_trace(fmt, args...)
Definition logging.h:370
#define pcmk__config_err(fmt...)
#define DEFAULT_REMOTE_KEY_LOCATION
Definition lrmd.h:55
#define DEFAULT_REMOTE_PORT
Definition lrmd.h:56
pcmk_scheduler_t * scheduler
void pcmk__free_node_copy(void *data)
Definition nodes.c:64
@ pcmk__probe_exclusive
@ pcmk__probe_never
xmlNode * crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name, const char *value)
Create an XML name/value pair.
Definition nvpair.c:301
#define pcmk__insert_meta(obj, name, value)
#define PCMK_META_PROMOTABLE
Definition options.h:102
#define PCMK_META_CLONE_NODE_MAX
Definition options.h:85
#define PCMK_META_CONTAINER_ATTRIBUTE_TARGET
Definition options.h:86
#define PCMK_VALUE_HOST
Definition options.h:162
#define PCMK_REMOTE_RA_ADDR
Definition options.h:123
#define PCMK_META_PROMOTED_MAX
Definition options.h:103
#define PCMK_META_CLONE_MAX
Definition options.h:83
#define PCMK_VALUE_TRUE
Definition options.h:219
#define PCMK_META_ORDERED
Definition options.h:100
#define PCMK_VALUE_REMOTE
Definition options.h:202
#define PCMK_META_GLOBALLY_UNIQUE
Definition options.h:90
#define PCMK_VALUE_FALSE
Definition options.h:154
#define PCMK__ENV_REMOTE_PORT
Control output from tools.
@ pcmk_show_implicit_rscs
Definition output.h:61
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition output_xml.c:566
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
const char * target
Definition pcmk_fence.c:31
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition utils.c:124
const char * pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts)
Definition pe_output.c:24
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition complex.c:1025
int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name,...) G_GNUC_NULL_TERMINATED
Definition pe_output.c:619
int pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, unsigned int options)
int pe__bundle_xml(pcmk__output_t *out, va_list args)
int pe__bundle_html(pcmk__output_t *out, va_list args)
int pe__bundle_text(pcmk__output_t *out, va_list args)
bool pe__count_active_node(const pcmk_resource_t *rsc, pcmk_node_t *node, pcmk_node_t **active, unsigned int *count_all, unsigned int *count_clean)
Definition complex.c:1116
void common_free(pcmk_resource_t *rsc)
Definition complex.c:1042
pcmk_node_t * pe_create_node(const char *id, const char *uname, const char *type, int score, pcmk_scheduler_t *scheduler)
Definition unpack.c:452
int pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, unsigned int options)
bool pcmk__rsc_filtered_by_node(pcmk_resource_t *rsc, GList *only_node)
Definition utils.c:782
bool xml_contains_remote_node(xmlNode *xml)
Definition remote.c:49
xmlNode * pe_create_remote_xml(xmlNode *parent, const char *uname, const char *container_id, const char *migrateable, const char *is_managed, const char *start_timeout, const char *server, const char *port)
Definition remote.c:128
@ pcmk__rsc_managed
@ pcmk__rsc_maintenance
@ pcmk__rsc_unique
@ pcmk__rsc_exclusive_probes
@ pcmk__rsc_replica_container
@ pcmk__rsc_notify
@ pcmk__rsc_remote_nesting_allowed
@ pcmk__rsc_failed
#define pcmk__set_rsc_flags(resource, flags_to_set)
void pcmk__free_resource(gpointer user_data)
Definition resources.c:25
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
@ pcmk_rc_no_output
Definition results.h:128
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_unpack_error
Definition results.h:122
#define pcmk__assert(expr)
rsc_role_e
Definition roles.h:34
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:35
pcmk_node_t * pcmk_find_node(const pcmk_scheduler_t *scheduler, const char *node_name)
Find a node by name in scheduler data.
Definition scheduler.c:282
#define pcmk__rsc_trace(rsc, fmt, args...)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:26
#define PCMK__SERVER_EXECD
#define PCMK__SERVER_REMOTED
Cluster status and scheduling.
pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id)
Definition status.c:191
const char * rsc_printable_id(const pcmk_resource_t *rsc)
Definition utils.c:581
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:498
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition strings.c:703
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:116
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition strings.c:984
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:685
@ pcmk__str_none
@ pcmk__str_star_matches
@ pcmk__str_casei
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1299
#define pcmk__str_copy(str)
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition strings.c:796
A single instance of a bundle.
pcmk_resource_t * child
Instance of bundled resource.
int offset
0-origin index of this instance in bundle
char * ipaddr
IP address associated with this instance.
pcmk_resource_t * remote
Pacemaker Remote connection into container.
pcmk_node_t * node
Copy of node created for this instance.
pcmk_resource_t * container
Container associated with this instance.
pcmk_resource_t * ip
IP address resource for ipaddr.
enum pcmk__probe_mode probe_mode
pcmk_resource_t * remote
This structure contains everything that makes up a single output formatter.
pcmk_scheduler_t * scheduler
const pcmk__rsc_methods_t * fns
unsigned long long flags
Definition resources.h:69
pcmk__resource_private_t * priv
Definition resources.h:61
bool(* is_filtered)(const pcmk_resource_t *rsc, const GList *only_rsc, bool check_parent)
pcmk_node_t *(* active_node)(const pcmk_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean)
bool(* active)(const pcmk_resource_t *rsc, bool all)
void(* count)(pcmk_resource_t *rsc)
pcmk__scheduler_private_t * priv
Definition scheduler.h:99
pcmk__node_private_t * priv
Definition nodes.h:85
struct pcmk__node_details * details
Definition nodes.h:82
struct pcmk__node_assignment * assign
Definition nodes.h:79
Wrappers for and extensions to libxml2.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
void pcmk__xe_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:832
void pcmk__xml_sanitize_id(char *id)
Definition xml.c:674
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
#define PCMK_XA_OPTIONS
Definition xml_names.h:350
#define PCMK_XE_DOCKER
Definition xml_names.h:103
#define PCMK_XA_DESCRIPTION
Definition xml_names.h:261
#define PCMK_XE_BUNDLE
Definition xml_names.h:72
#define PCMK_XA_CLASS
Definition xml_names.h:246
#define PCMK_XA_IP_RANGE_START
Definition xml_names.h:310
#define PCMK_XE_REPLICA
Definition xml_names.h:171
#define PCMK_XA_FAILED
Definition xml_names.h:283
#define PCMK_XA_RANGE
Definition xml_names.h:368
#define PCMK_XE_PORT_MAPPING
Definition xml_names.h:162
#define PCMK_XA_HOST_INTERFACE
Definition xml_names.h:298
#define PCMK_XA_HOST_NETMASK
Definition xml_names.h:299
#define PCMK_XA_ADD_HOST
Definition xml_names.h:231
#define PCMK_XA_NETWORK
Definition xml_names.h:331
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_PROVIDER
Definition xml_names.h:364
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition xml_names.h:122
#define PCMK_XE_PODMAN
Definition xml_names.h:161
#define PCMK_XE_STORAGE_MAPPING
Definition xml_names.h:206
#define PCMK_XA_MAINTENANCE
Definition xml_names.h:321
#define PCMK_XE_META_ATTRIBUTES
Definition xml_names.h:130
#define PCMK_XA_INTERNAL_PORT
Definition xml_names.h:308
#define PCMK_XE_NETWORK
Definition xml_names.h:135
#define PCMK_XA_PROMOTED_MAX
Definition xml_names.h:362
#define PCMK_XA_TARGET_DIR
Definition xml_names.h:420
#define PCMK_XA_RUN_COMMAND
Definition xml_names.h:392
#define PCMK_XA_IMAGE
Definition xml_names.h:304
#define PCMK_XE_CLONE
Definition xml_names.h:80
#define PCMK_XA_CONTROL_PORT
Definition xml_names.h:251
#define PCMK_XE_PRIMITIVE
Definition xml_names.h:164
#define PCMK_XA_TYPE
Definition xml_names.h:430
#define PCMK_XA_PORT
Definition xml_names.h:357
#define PCMK_XA_SOURCE_DIR
Definition xml_names.h:402
#define PCMK_XE_STORAGE
Definition xml_names.h:205
#define PCMK_XA_SOURCE_DIR_ROOT
Definition xml_names.h:403
#define PCMK_XA_REPLICAS
Definition xml_names.h:378
#define PCMK_XA_UNIQUE
Definition xml_names.h:434
#define PCMK_XE_OPERATIONS
Definition xml_names.h:151
#define PCMK_XA_MANAGED
Definition xml_names.h:323
#define PCMK_XA_REPLICAS_PER_HOST
Definition xml_names.h:379