pacemaker 2.1.7-2.1.7
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
bundle.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2023 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 <stdint.h>
14
15#include <crm/pengine/rules.h>
16#include <crm/pengine/status.h>
18#include <crm/msg_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
48
49#define PE__CONTAINER_AGENT_UNKNOWN_S "unknown"
50#define PE__CONTAINER_AGENT_DOCKER_S "docker"
51#define PE__CONTAINER_AGENT_RKT_S "rkt"
52#define PE__CONTAINER_AGENT_PODMAN_S "podman"
53
54typedef struct pe__bundle_variant_data_s {
55 int promoted_max;
56 int nreplicas;
57 int nreplicas_per_host;
58 char *prefix;
59 char *image;
60 const char *ip_last;
61 char *host_network;
62 char *host_netmask;
63 char *control_port;
64 char *container_network;
65 char *ip_range_start;
66 gboolean add_host;
67 gchar *container_host_options;
68 char *container_command;
69 char *launcher_options;
70 const char *attribute_target;
71
72 pcmk_resource_t *child;
73
74 GList *replicas; // pe__bundle_replica_t *
75 GList *ports; // pe__bundle_port_t *
76 GList *mounts; // pe__bundle_mount_t *
77
78 enum pe__container_agent agent_type;
80
81#define get_bundle_variant_data(data, rsc) \
82 CRM_ASSERT(rsc != NULL); \
83 CRM_ASSERT(rsc->variant == pcmk_rsc_variant_bundle); \
84 CRM_ASSERT(rsc->variant_opaque != NULL); \
85 data = (pe__bundle_variant_data_t *) rsc->variant_opaque;
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 ((top == NULL) || (top->variant != pcmk_rsc_variant_bundle)) {
137 return NULL;
138 }
140
141 for (const GList *iter = data->replicas; iter != NULL; iter = iter->next) {
142 const pe__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 pe__bundle_replica_t *replica = iter->data;
169
170 if (pe__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 pe__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)(pe__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((pe__bundle_replica_t *) iter->data, user_data)) {
219 break;
220 }
221 }
222}
223
233void
235 bool (*fn)(const pe__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 pe__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
308 g_string_append_printf(buffer, " --hosts-entry=%s=%s-%d",
309 replica->ipaddr, data->prefix,
310 replica->offset);
311 break;
312
313 default: // PE__CONTAINER_AGENT_UNKNOWN
314 break;
315 }
316}
317
318static xmlNode *
319create_resource(const char *name, const char *provider, const char *kind)
320{
321 xmlNode *rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE);
322
325 crm_xml_add(rsc, XML_AGENT_ATTR_PROVIDER, provider);
326 crm_xml_add(rsc, XML_ATTR_TYPE, kind);
327
328 return rsc;
329}
330
343static bool
344valid_network(pe__bundle_variant_data_t *data)
345{
346 if(data->ip_range_start) {
347 return TRUE;
348 }
349 if(data->control_port) {
350 if(data->nreplicas_per_host > 1) {
351 pe_err("Specifying the 'control-port' for %s requires 'replicas-per-host=1'", data->prefix);
352 data->nreplicas_per_host = 1;
353 // @TODO to be sure:
354 // pe__clear_resource_flags(rsc, pcmk_rsc_unique);
355 }
356 return TRUE;
357 }
358 return FALSE;
359}
360
361static int
363 pe__bundle_replica_t *replica)
364{
365 if(data->ip_range_start) {
366 char *id = NULL;
367 xmlNode *xml_ip = NULL;
368 xmlNode *xml_obj = NULL;
369
370 id = crm_strdup_printf("%s-ip-%s", data->prefix, replica->ipaddr);
372 xml_ip = create_resource(id, "heartbeat", "IPaddr2");
373 free(id);
374
375 xml_obj = create_xml_node(xml_ip, XML_TAG_ATTR_SETS);
376 crm_xml_set_id(xml_obj, "%s-attributes-%d",
377 data->prefix, replica->offset);
378
379 crm_create_nvpair_xml(xml_obj, NULL, "ip", replica->ipaddr);
380 if(data->host_network) {
381 crm_create_nvpair_xml(xml_obj, NULL, "nic", data->host_network);
382 }
383
384 if(data->host_netmask) {
385 crm_create_nvpair_xml(xml_obj, NULL,
386 "cidr_netmask", data->host_netmask);
387
388 } else {
389 crm_create_nvpair_xml(xml_obj, NULL, "cidr_netmask", "32");
390 }
391
392 xml_obj = create_xml_node(xml_ip, "operations");
393 crm_create_op_xml(xml_obj, ID(xml_ip), PCMK_ACTION_MONITOR, "60s",
394 NULL);
395
396 // TODO: Other ops? Timeouts and intervals from underlying resource?
397
398 if (pe__unpack_resource(xml_ip, &replica->ip, parent,
401 }
402
403 parent->children = g_list_append(parent->children, replica->ip);
404 }
405 return pcmk_rc_ok;
406}
407
408static const char*
409container_agent_str(enum pe__container_agent t)
410{
411 switch (t) {
415 default: // PE__CONTAINER_AGENT_UNKNOWN
416 break;
417 }
419}
420
421static int
422create_container_resource(pcmk_resource_t *parent,
424 pe__bundle_replica_t *replica)
425{
426 char *id = NULL;
427 xmlNode *xml_container = NULL;
428 xmlNode *xml_obj = NULL;
429
430 // Agent-specific
431 const char *hostname_opt = NULL;
432 const char *env_opt = NULL;
433 const char *agent_str = NULL;
434 int volid = 0; // rkt-only
435
436 GString *buffer = NULL;
437 GString *dbuffer = NULL;
438
439 // Where syntax differences are drop-in replacements, set them now
440 switch (data->agent_type) {
443 hostname_opt = "-h ";
444 env_opt = "-e ";
445 break;
447 hostname_opt = "--hostname=";
448 env_opt = "--environment=";
449 break;
450 default: // PE__CONTAINER_AGENT_UNKNOWN
452 }
453 agent_str = container_agent_str(data->agent_type);
454
455 buffer = g_string_sized_new(4096);
456
457 id = crm_strdup_printf("%s-%s-%d", data->prefix, agent_str,
458 replica->offset);
460 xml_container = create_resource(id, "heartbeat", agent_str);
461 free(id);
462
463 xml_obj = create_xml_node(xml_container, XML_TAG_ATTR_SETS);
464 crm_xml_set_id(xml_obj, "%s-attributes-%d", data->prefix, replica->offset);
465
466 crm_create_nvpair_xml(xml_obj, NULL, "image", data->image);
467 crm_create_nvpair_xml(xml_obj, NULL, "allow_pull", XML_BOOLEAN_TRUE);
468 crm_create_nvpair_xml(xml_obj, NULL, "force_kill", XML_BOOLEAN_FALSE);
469 crm_create_nvpair_xml(xml_obj, NULL, "reuse", XML_BOOLEAN_FALSE);
470
471 if (data->agent_type == PE__CONTAINER_AGENT_DOCKER) {
472 g_string_append(buffer, " --restart=no");
473 }
474
475 /* Set a container hostname only if we have an IP to map it to. The user can
476 * set -h or --uts=host themselves if they want a nicer name for logs, but
477 * this makes applications happy who need their hostname to match the IP
478 * they bind to.
479 */
480 if (data->ip_range_start != NULL) {
481 g_string_append_printf(buffer, " %s%s-%d", hostname_opt, data->prefix,
482 replica->offset);
483 }
484 pcmk__g_strcat(buffer, " ", env_opt, "PCMK_stderr=1", NULL);
485
486 if (data->container_network != NULL) {
487 pcmk__g_strcat(buffer, " --net=", data->container_network, NULL);
488 }
489
490 if (data->control_port != NULL) {
491 pcmk__g_strcat(buffer, " ", env_opt, "PCMK_" PCMK__ENV_REMOTE_PORT "=",
492 data->control_port, NULL);
493 } else {
494 g_string_append_printf(buffer, " %sPCMK_" PCMK__ENV_REMOTE_PORT "=%d",
495 env_opt, DEFAULT_REMOTE_PORT);
496 }
497
498 for (GList *iter = data->mounts; iter != NULL; iter = iter->next) {
499 pe__bundle_mount_t *mount = (pe__bundle_mount_t *) iter->data;
500 char *source = NULL;
501
502 if (pcmk_is_set(mount->flags, pe__bundle_mount_subdir)) {
503 source = crm_strdup_printf("%s/%s-%d", mount->source, data->prefix,
504 replica->offset);
505 pcmk__add_separated_word(&dbuffer, 1024, source, ",");
506 }
507
508 switch (data->agent_type) {
511 pcmk__g_strcat(buffer,
512 " -v ", pcmk__s(source, mount->source),
513 ":", mount->target, NULL);
514
515 if (mount->options != NULL) {
516 pcmk__g_strcat(buffer, ":", mount->options, NULL);
517 }
518 break;
520 g_string_append_printf(buffer,
521 " --volume vol%d,kind=host,"
522 "source=%s%s%s "
523 "--mount volume=vol%d,target=%s",
524 volid, pcmk__s(source, mount->source),
525 (mount->options != NULL)? "," : "",
526 pcmk__s(mount->options, ""),
527 volid, mount->target);
528 volid++;
529 break;
530 default:
531 break;
532 }
533 free(source);
534 }
535
536 for (GList *iter = data->ports; iter != NULL; iter = iter->next) {
537 pe__bundle_port_t *port = (pe__bundle_port_t *) iter->data;
538
539 switch (data->agent_type) {
542 if (replica->ipaddr != NULL) {
543 pcmk__g_strcat(buffer,
544 " -p ", replica->ipaddr, ":", port->source,
545 ":", port->target, NULL);
546
547 } else if (!pcmk__str_eq(data->container_network, "host",
549 // No need to do port mapping if net == host
550 pcmk__g_strcat(buffer,
551 " -p ", port->source, ":", port->target,
552 NULL);
553 }
554 break;
556 if (replica->ipaddr != NULL) {
557 pcmk__g_strcat(buffer,
558 " --port=", port->target,
559 ":", replica->ipaddr, ":", port->source,
560 NULL);
561 } else {
562 pcmk__g_strcat(buffer,
563 " --port=", port->target, ":", port->source,
564 NULL);
565 }
566 break;
567 default:
568 break;
569 }
570 }
571
572 /* @COMPAT: We should use pcmk__add_word() here, but we can't yet, because
573 * it would cause restarts during rolling upgrades.
574 *
575 * In a previous version of the container resource creation logic, if
576 * data->launcher_options is not NULL, we append
577 * (" %s", data->launcher_options) even if data->launcher_options is an
578 * empty string. Likewise for data->container_host_options. Using
579 *
580 * pcmk__add_word(buffer, 0, data->launcher_options)
581 *
582 * removes that extra trailing space, causing a resource definition change.
583 */
584 if (data->launcher_options != NULL) {
585 pcmk__g_strcat(buffer, " ", data->launcher_options, NULL);
586 }
587
588 if (data->container_host_options != NULL) {
589 pcmk__g_strcat(buffer, " ", data->container_host_options, NULL);
590 }
591
592 crm_create_nvpair_xml(xml_obj, NULL, "run_opts",
593 (const char *) buffer->str);
594 g_string_free(buffer, TRUE);
595
596 crm_create_nvpair_xml(xml_obj, NULL, "mount_points",
597 (dbuffer != NULL)? (const char *) dbuffer->str : "");
598 if (dbuffer != NULL) {
599 g_string_free(dbuffer, TRUE);
600 }
601
602 if (replica->child != NULL) {
603 if (data->container_command != NULL) {
604 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
605 data->container_command);
606 } else {
607 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
608 SBIN_DIR "/pacemaker-remoted");
609 }
610
611 /* TODO: Allow users to specify their own?
612 *
613 * We just want to know if the container is alive; we'll monitor the
614 * child independently.
615 */
616 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true");
617#if 0
618 /* @TODO Consider supporting the use case where we can start and stop
619 * resources, but not proxy local commands (such as setting node
620 * attributes), by running the local executor in stand-alone mode.
621 * However, this would probably be better done via ACLs as with other
622 * Pacemaker Remote nodes.
623 */
624 } else if ((child != NULL) && data->untrusted) {
625 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
626 CRM_DAEMON_DIR "/pacemaker-execd");
627 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd",
628 CRM_DAEMON_DIR "/pacemaker/cts-exec-helper -c poke");
629#endif
630 } else {
631 if (data->container_command != NULL) {
632 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
633 data->container_command);
634 }
635
636 /* TODO: Allow users to specify their own?
637 *
638 * We don't know what's in the container, so we just want to know if it
639 * is alive.
640 */
641 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true");
642 }
643
644 xml_obj = create_xml_node(xml_container, "operations");
645 crm_create_op_xml(xml_obj, ID(xml_container), PCMK_ACTION_MONITOR, "60s",
646 NULL);
647
648 // TODO: Other ops? Timeouts and intervals from underlying resource?
649 if (pe__unpack_resource(xml_container, &replica->container, parent,
652 }
654 parent->children = g_list_append(parent->children, replica->container);
655
656 return pcmk_rc_ok;
657}
658
665static void
666disallow_node(pcmk_resource_t *rsc, const char *uname)
667{
668 gpointer match = g_hash_table_lookup(rsc->allowed_nodes, uname);
669
670 if (match) {
671 ((pcmk_node_t *) match)->weight = -INFINITY;
672 ((pcmk_node_t *) match)->rsc_discover_mode = pcmk_probe_never;
673 }
674 if (rsc->children) {
675 g_list_foreach(rsc->children, (GFunc) disallow_node, (gpointer) uname);
676 }
677}
678
679static int
680create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data,
681 pe__bundle_replica_t *replica)
682{
683 if (replica->child && valid_network(data)) {
684 GHashTableIter gIter;
685 pcmk_node_t *node = NULL;
686 xmlNode *xml_remote = NULL;
687 char *id = crm_strdup_printf("%s-%d", data->prefix, replica->offset);
688 char *port_s = NULL;
689 const char *uname = NULL;
690 const char *connect_name = NULL;
691
692 if (pe_find_resource(parent->cluster->resources, id) != NULL) {
693 free(id);
694 // The biggest hammer we have
695 id = crm_strdup_printf("pcmk-internal-%s-remote-%d",
696 replica->child->id, replica->offset);
697 //@TODO return error instead of asserting?
698 CRM_ASSERT(pe_find_resource(parent->cluster->resources,
699 id) == NULL);
700 }
701
702 /* REMOTE_CONTAINER_HACK: Using "#uname" as the server name when the
703 * connection does not have its own IP is a magic string that we use to
704 * support nested remotes (i.e. a bundle running on a remote node).
705 */
706 connect_name = (replica->ipaddr? replica->ipaddr : "#uname");
707
708 if (data->control_port == NULL) {
709 port_s = pcmk__itoa(DEFAULT_REMOTE_PORT);
710 }
711
712 /* This sets replica->container as replica->remote's container, which is
713 * similar to what happens with guest nodes. This is how the scheduler
714 * knows that the bundle node is fenced by recovering the container, and
715 * that remote should be ordered relative to the container.
716 */
717 xml_remote = pe_create_remote_xml(NULL, id, replica->container->id,
718 NULL, NULL, NULL,
719 connect_name, (data->control_port?
720 data->control_port : port_s));
721 free(port_s);
722
723 /* Abandon our created ID, and pull the copy from the XML, because we
724 * need something that will get freed during scheduler data cleanup to
725 * use as the node ID and uname.
726 */
727 free(id);
728 id = NULL;
729 uname = ID(xml_remote);
730
731 /* Ensure a node has been created for the guest (it may have already
732 * been, if it has a permanent node attribute), and ensure its weight is
733 * -INFINITY so no other resources can run on it.
734 */
735 node = pe_find_node(parent->cluster->nodes, uname);
736 if (node == NULL) {
737 node = pe_create_node(uname, uname, "remote", "-INFINITY",
738 parent->cluster);
739 } else {
740 node->weight = -INFINITY;
741 }
743
744 /* unpack_remote_nodes() ensures that each remote node and guest node
745 * has a pcmk_node_t entry. Ideally, it would do the same for bundle
746 * nodes. Unfortunately, a bundle has to be mostly unpacked before it's
747 * obvious what nodes will be needed, so we do it just above.
748 *
749 * Worse, that means that the node may have been utilized while
750 * unpacking other resources, without our weight correction. The most
751 * likely place for this to happen is when pe__unpack_resource() calls
752 * resource_location() to set a default score in symmetric clusters.
753 * This adds a node *copy* to each resource's allowed nodes, and these
754 * copies will have the wrong weight.
755 *
756 * As a hacky workaround, fix those copies here.
757 *
758 * @TODO Possible alternative: ensure bundles are unpacked before other
759 * resources, so the weight is correct before any copies are made.
760 */
761 g_list_foreach(parent->cluster->resources, (GFunc) disallow_node,
762 (gpointer) uname);
763
764 replica->node = pe__copy_node(node);
765 replica->node->weight = 500;
767
768 /* Ensure the node shows up as allowed and with the correct discovery set */
769 if (replica->child->allowed_nodes != NULL) {
770 g_hash_table_destroy(replica->child->allowed_nodes);
771 }
772 replica->child->allowed_nodes = pcmk__strkey_table(NULL, free);
773 g_hash_table_insert(replica->child->allowed_nodes,
774 (gpointer) replica->node->details->id,
775 pe__copy_node(replica->node));
776
777 {
778 pcmk_node_t *copy = pe__copy_node(replica->node);
779 copy->weight = -INFINITY;
780 g_hash_table_insert(replica->child->parent->allowed_nodes,
781 (gpointer) replica->node->details->id, copy);
782 }
783 if (pe__unpack_resource(xml_remote, &replica->remote, parent,
786 }
787
788 g_hash_table_iter_init(&gIter, replica->remote->allowed_nodes);
789 while (g_hash_table_iter_next(&gIter, NULL, (void **)&node)) {
790 if (pe__is_guest_or_remote_node(node)) {
791 /* Remote resources can only run on 'normal' cluster node */
792 node->weight = -INFINITY;
793 }
794 }
795
796 replica->node->details->remote_rsc = replica->remote;
797
798 // Ensure pe__is_guest_node() functions correctly immediately
799 replica->remote->container = replica->container;
800
801 /* A bundle's #kind is closer to "container" (guest node) than the
802 * "remote" set by pe_create_node().
803 */
804 g_hash_table_insert(replica->node->details->attrs,
805 strdup(CRM_ATTR_KIND), strdup("container"));
806
807 /* One effect of this is that setup_container() will add
808 * replica->remote to replica->container's fillers, which will make
809 * pe__resource_contains_guest_node() true for replica->container.
810 *
811 * replica->child does NOT get added to replica->container's fillers.
812 * The only noticeable effect if it did would be for its fail count to
813 * be taken into account when checking replica->container's migration
814 * threshold.
815 */
816 parent->children = g_list_append(parent->children, replica->remote);
817 }
818 return pcmk_rc_ok;
819}
820
821static int
822create_replica_resources(pcmk_resource_t *parent, pe__bundle_variant_data_t *data,
823 pe__bundle_replica_t *replica)
824{
825 int rc = pcmk_rc_ok;
826
827 rc = create_container_resource(parent, data, replica);
828 if (rc != pcmk_rc_ok) {
829 return rc;
830 }
831
832 rc = create_ip_resource(parent, data, replica);
833 if (rc != pcmk_rc_ok) {
834 return rc;
835 }
836
837 rc = create_remote_resource(parent, data, replica);
838 if (rc != pcmk_rc_ok) {
839 return rc;
840 }
841
842 if ((replica->child != NULL) && (replica->ipaddr != NULL)) {
843 add_hash_param(replica->child->meta, "external-ip", replica->ipaddr);
844 }
845
846 if (replica->remote != NULL) {
847 /*
848 * Allow the remote connection resource to be allocated to a
849 * different node than the one on which the container is active.
850 *
851 * This makes it possible to have Pacemaker Remote nodes running
852 * containers with pacemaker-remoted inside in order to start
853 * services inside those containers.
854 */
857 }
858 return rc;
859}
860
861static void
862mount_add(pe__bundle_variant_data_t *bundle_data, const char *source,
863 const char *target, const char *options, uint32_t flags)
864{
865 pe__bundle_mount_t *mount = calloc(1, sizeof(pe__bundle_mount_t));
866
867 CRM_ASSERT(mount != NULL);
868 mount->source = strdup(source);
869 mount->target = strdup(target);
870 pcmk__str_update(&mount->options, options);
871 mount->flags = flags;
872 bundle_data->mounts = g_list_append(bundle_data->mounts, mount);
873}
874
875static void
876mount_free(pe__bundle_mount_t *mount)
877{
878 free(mount->source);
879 free(mount->target);
880 free(mount->options);
881 free(mount);
882}
883
884static void
885port_free(pe__bundle_port_t *port)
886{
887 free(port->source);
888 free(port->target);
889 free(port);
890}
891
893replica_for_remote(pcmk_resource_t *remote)
894{
895 pcmk_resource_t *top = remote;
896 pe__bundle_variant_data_t *bundle_data = NULL;
897
898 if (top == NULL) {
899 return NULL;
900 }
901
902 while (top->parent != NULL) {
903 top = top->parent;
904 }
905
906 get_bundle_variant_data(bundle_data, top);
907 for (GList *gIter = bundle_data->replicas; gIter != NULL;
908 gIter = gIter->next) {
909 pe__bundle_replica_t *replica = gIter->data;
910
911 if (replica->remote == remote) {
912 return replica;
913 }
914 }
915 CRM_LOG_ASSERT(FALSE);
916 return NULL;
917}
918
919bool
921{
922 const char *value;
923 GHashTable *params = NULL;
924
925 if (rsc == NULL) {
926 return false;
927 }
928
929 // Use NULL node since pcmk__bundle_expand() uses that to set value
930 params = pe_rsc_params(rsc, NULL, rsc->cluster);
931 value = g_hash_table_lookup(params, XML_RSC_ATTR_REMOTE_RA_ADDR);
932
933 return pcmk__str_eq(value, "#uname", pcmk__str_casei)
935}
936
937const char *
939 xmlNode *xml, const char *field)
940{
941 // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
942
943 pcmk_node_t *node = NULL;
944 pe__bundle_replica_t *replica = NULL;
945
947 return NULL;
948 }
949
950 replica = replica_for_remote(rsc);
951 if (replica == NULL) {
952 return NULL;
953 }
954
955 node = replica->container->allocated_to;
956 if (node == NULL) {
957 /* If it won't be running anywhere after the
958 * transition, go with where it's running now.
959 */
960 node = pe__current_node(replica->container);
961 }
962
963 if(node == NULL) {
964 crm_trace("Cannot determine address for bundle connection %s", rsc->id);
965 return NULL;
966 }
967
968 crm_trace("Setting address for bundle connection %s to bundle host %s",
969 rsc->id, pe__node_name(node));
970 if(xml != NULL && field != NULL) {
971 crm_xml_add(xml, field, node->details->uname);
972 }
973
974 return node->details->uname;
975}
976
977#define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set) do { \
978 flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \
979 "Bundle mount", ID(mount_xml), flags, \
980 (flags_to_set), #flags_to_set); \
981 } while (0)
982
983gboolean
985{
986 const char *value = NULL;
987 xmlNode *xml_obj = NULL;
988 xmlNode *xml_resource = NULL;
989 pe__bundle_variant_data_t *bundle_data = NULL;
990 bool need_log_mount = TRUE;
991
992 CRM_ASSERT(rsc != NULL);
993 pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
994
995 bundle_data = calloc(1, sizeof(pe__bundle_variant_data_t));
996 rsc->variant_opaque = bundle_data;
997 bundle_data->prefix = strdup(rsc->id);
998
1000 if (xml_obj != NULL) {
1001 bundle_data->agent_type = PE__CONTAINER_AGENT_DOCKER;
1002 } else {
1004 if (xml_obj != NULL) {
1005 bundle_data->agent_type = PE__CONTAINER_AGENT_RKT;
1006 } else {
1008 if (xml_obj != NULL) {
1009 bundle_data->agent_type = PE__CONTAINER_AGENT_PODMAN;
1010 } else {
1011 return FALSE;
1012 }
1013 }
1014 }
1015
1016 // Use 0 for default, minimum, and invalid promoted-max
1017 value = crm_element_value(xml_obj, PCMK_META_PROMOTED_MAX);
1018 if (value == NULL) {
1019 // @COMPAT deprecated since 2.0.0
1020 value = crm_element_value(xml_obj, "masters");
1021 }
1022 pcmk__scan_min_int(value, &bundle_data->promoted_max, 0);
1023
1024 // Default replicas to promoted-max if it was specified and 1 otherwise
1025 value = crm_element_value(xml_obj, "replicas");
1026 if ((value == NULL) && (bundle_data->promoted_max > 0)) {
1027 bundle_data->nreplicas = bundle_data->promoted_max;
1028 } else {
1029 pcmk__scan_min_int(value, &bundle_data->nreplicas, 1);
1030 }
1031
1032 /*
1033 * Communication between containers on the same host via the
1034 * floating IPs only works if the container is started with:
1035 * --userland-proxy=false --ip-masq=false
1036 */
1037 value = crm_element_value(xml_obj, "replicas-per-host");
1038 pcmk__scan_min_int(value, &bundle_data->nreplicas_per_host, 1);
1039 if (bundle_data->nreplicas_per_host == 1) {
1041 }
1042
1043 bundle_data->container_command = crm_element_value_copy(xml_obj, "run-command");
1044 bundle_data->launcher_options = crm_element_value_copy(xml_obj, "options");
1045 bundle_data->image = crm_element_value_copy(xml_obj, "image");
1046 bundle_data->container_network = crm_element_value_copy(xml_obj, "network");
1047
1048 xml_obj = first_named_child(rsc->xml, "network");
1049 if(xml_obj) {
1050
1051 bundle_data->ip_range_start = crm_element_value_copy(xml_obj, "ip-range-start");
1052 bundle_data->host_netmask = crm_element_value_copy(xml_obj, "host-netmask");
1053 bundle_data->host_network = crm_element_value_copy(xml_obj, "host-interface");
1054 bundle_data->control_port = crm_element_value_copy(xml_obj, "control-port");
1055 value = crm_element_value(xml_obj, "add-host");
1056 if (crm_str_to_boolean(value, &bundle_data->add_host) != 1) {
1057 bundle_data->add_host = TRUE;
1058 }
1059
1060 for (xmlNode *xml_child = pcmk__xe_first_child(xml_obj); xml_child != NULL;
1061 xml_child = pcmk__xe_next(xml_child)) {
1062
1063 pe__bundle_port_t *port = calloc(1, sizeof(pe__bundle_port_t));
1064 port->source = crm_element_value_copy(xml_child, "port");
1065
1066 if(port->source == NULL) {
1067 port->source = crm_element_value_copy(xml_child, "range");
1068 } else {
1069 port->target = crm_element_value_copy(xml_child, "internal-port");
1070 }
1071
1072 if(port->source != NULL && strlen(port->source) > 0) {
1073 if(port->target == NULL) {
1074 port->target = strdup(port->source);
1075 }
1076 bundle_data->ports = g_list_append(bundle_data->ports, port);
1077
1078 } else {
1079 pe_err("Invalid port directive %s", ID(xml_child));
1080 port_free(port);
1081 }
1082 }
1083 }
1084
1085 xml_obj = first_named_child(rsc->xml, "storage");
1086 for (xmlNode *xml_child = pcmk__xe_first_child(xml_obj); xml_child != NULL;
1087 xml_child = pcmk__xe_next(xml_child)) {
1088
1089 const char *source = crm_element_value(xml_child, "source-dir");
1090 const char *target = crm_element_value(xml_child, "target-dir");
1091 const char *options = crm_element_value(xml_child, "options");
1093
1094 if (source == NULL) {
1095 source = crm_element_value(xml_child, "source-dir-root");
1098 }
1099
1100 if (source && target) {
1101 mount_add(bundle_data, source, target, options, flags);
1102 if (strcmp(target, "/var/log") == 0) {
1103 need_log_mount = FALSE;
1104 }
1105 } else {
1106 pe_err("Invalid mount directive %s", ID(xml_child));
1107 }
1108 }
1109
1110 xml_obj = first_named_child(rsc->xml, "primitive");
1111 if (xml_obj && valid_network(bundle_data)) {
1112 char *value = NULL;
1113 xmlNode *xml_set = NULL;
1114
1115 xml_resource = create_xml_node(NULL, XML_CIB_TAG_INCARNATION);
1116
1117 /* @COMPAT We no longer use the <master> tag, but we need to keep it as
1118 * part of the resource name, so that bundles don't restart in a rolling
1119 * upgrade. (It also avoids needing to change regression tests.)
1120 */
1121 crm_xml_set_id(xml_resource, "%s-%s", bundle_data->prefix,
1122 (bundle_data->promoted_max? "master"
1123 : (const char *)xml_resource->name));
1124
1125 xml_set = create_xml_node(xml_resource, XML_TAG_META_SETS);
1126 crm_xml_set_id(xml_set, "%s-%s-meta", bundle_data->prefix, xml_resource->name);
1127
1128 crm_create_nvpair_xml(xml_set, NULL,
1130
1131 value = pcmk__itoa(bundle_data->nreplicas);
1132 crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_MAX, value);
1133 free(value);
1134
1135 value = pcmk__itoa(bundle_data->nreplicas_per_host);
1136 crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_NODE_MAX, value);
1137 free(value);
1138
1140 pcmk__btoa(bundle_data->nreplicas_per_host > 1));
1141
1142 if (bundle_data->promoted_max) {
1143 crm_create_nvpair_xml(xml_set, NULL,
1145
1146 value = pcmk__itoa(bundle_data->promoted_max);
1147 crm_create_nvpair_xml(xml_set, NULL, PCMK_META_PROMOTED_MAX, value);
1148 free(value);
1149 }
1150
1151 //crm_xml_add(xml_obj, XML_ATTR_ID, bundle_data->prefix);
1152 add_node_copy(xml_resource, xml_obj);
1153
1154 } else if(xml_obj) {
1155 pe_err("Cannot control %s inside %s without either ip-range-start or control-port",
1156 rsc->id, ID(xml_obj));
1157 return FALSE;
1158 }
1159
1160 if(xml_resource) {
1161 int lpc = 0;
1162 GList *childIter = NULL;
1163 pe__bundle_port_t *port = NULL;
1164 GString *buffer = NULL;
1165
1166 if (pe__unpack_resource(xml_resource, &(bundle_data->child), rsc,
1167 scheduler) != pcmk_rc_ok) {
1168 return FALSE;
1169 }
1170
1171 /* Currently, we always map the default authentication key location
1172 * into the same location inside the container.
1173 *
1174 * Ideally, we would respect the host's PCMK_authkey_location, but:
1175 * - it may be different on different nodes;
1176 * - the actual connection will do extra checking to make sure the key
1177 * file exists and is readable, that we can't do here on the DC
1178 * - tools such as crm_resource and crm_simulate may not have the same
1179 * environment variables as the cluster, causing operation digests to
1180 * differ
1181 *
1182 * Always using the default location inside the container is fine,
1183 * because we control the pacemaker_remote environment, and it avoids
1184 * having to pass another environment variable to the container.
1185 *
1186 * @TODO A better solution may be to have only pacemaker_remote use the
1187 * environment variable, and have the cluster nodes use a new
1188 * cluster option for key location. This would introduce the limitation
1189 * of the location being the same on all cluster nodes, but that's
1190 * reasonable.
1191 */
1192 mount_add(bundle_data, DEFAULT_REMOTE_KEY_LOCATION,
1194
1195 if (need_log_mount) {
1196 mount_add(bundle_data, CRM_BUNDLE_DIR, "/var/log", NULL,
1198 }
1199
1200 port = calloc(1, sizeof(pe__bundle_port_t));
1201 if(bundle_data->control_port) {
1202 port->source = strdup(bundle_data->control_port);
1203 } else {
1204 /* If we wanted to respect PCMK_remote_port, we could use
1205 * crm_default_remote_port() here and elsewhere in this file instead
1206 * of DEFAULT_REMOTE_PORT.
1207 *
1208 * However, it gains nothing, since we control both the container
1209 * environment and the connection resource parameters, and the user
1210 * can use a different port if desired by setting control-port.
1211 */
1212 port->source = pcmk__itoa(DEFAULT_REMOTE_PORT);
1213 }
1214 port->target = strdup(port->source);
1215 bundle_data->ports = g_list_append(bundle_data->ports, port);
1216
1217 buffer = g_string_sized_new(1024);
1218 for (childIter = bundle_data->child->children; childIter != NULL;
1219 childIter = childIter->next) {
1220
1221 pe__bundle_replica_t *replica = calloc(1, sizeof(pe__bundle_replica_t));
1222
1223 replica->child = childIter->data;
1224 replica->child->exclusive_discover = TRUE;
1225 replica->offset = lpc++;
1226
1227 // Ensure the child's notify gets set based on the underlying primitive's value
1228 if (pcmk_is_set(replica->child->flags, pcmk_rsc_notify)) {
1229 pe__set_resource_flags(bundle_data->child, pcmk_rsc_notify);
1230 }
1231
1232 allocate_ip(bundle_data, replica, buffer);
1233 bundle_data->replicas = g_list_append(bundle_data->replicas,
1234 replica);
1235 bundle_data->attribute_target = g_hash_table_lookup(replica->child->meta,
1237 }
1238 bundle_data->container_host_options = g_string_free(buffer, FALSE);
1239
1240 if (bundle_data->attribute_target) {
1241 g_hash_table_replace(rsc->meta, strdup(XML_RSC_ATTR_TARGET),
1242 strdup(bundle_data->attribute_target));
1243 g_hash_table_replace(bundle_data->child->meta,
1244 strdup(XML_RSC_ATTR_TARGET),
1245 strdup(bundle_data->attribute_target));
1246 }
1247
1248 } else {
1249 // Just a naked container, no pacemaker-remote
1250 GString *buffer = g_string_sized_new(1024);
1251
1252 for (int lpc = 0; lpc < bundle_data->nreplicas; lpc++) {
1253 pe__bundle_replica_t *replica = calloc(1, sizeof(pe__bundle_replica_t));
1254
1255 replica->offset = lpc;
1256 allocate_ip(bundle_data, replica, buffer);
1257 bundle_data->replicas = g_list_append(bundle_data->replicas,
1258 replica);
1259 }
1260 bundle_data->container_host_options = g_string_free(buffer, FALSE);
1261 }
1262
1263 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1264 gIter = gIter->next) {
1265 pe__bundle_replica_t *replica = gIter->data;
1266
1267 if (create_replica_resources(rsc, bundle_data, replica) != pcmk_rc_ok) {
1268 pe_err("Failed unpacking resource %s", rsc->id);
1269 rsc->fns->free(rsc);
1270 return FALSE;
1271 }
1272
1273 /* Utilization needs special handling for bundles. It makes no sense for
1274 * the inner primitive to have utilization, because it is tied
1275 * one-to-one to the guest node created by the container resource -- and
1276 * there's no way to set capacities for that guest node anyway.
1277 *
1278 * What the user really wants is to configure utilization for the
1279 * container. However, the schema only allows utilization for
1280 * primitives, and the container resource is implicit anyway, so the
1281 * user can *only* configure utilization for the inner primitive. If
1282 * they do, move the primitive's utilization values to the container.
1283 *
1284 * @TODO This means that bundles without an inner primitive can't have
1285 * utilization. An alternative might be to allow utilization values in
1286 * the top-level bundle XML in the schema, and copy those to each
1287 * container.
1288 */
1289 if (replica->child != NULL) {
1290 GHashTable *empty = replica->container->utilization;
1291
1292 replica->container->utilization = replica->child->utilization;
1293 replica->child->utilization = empty;
1294 }
1295 }
1296
1297 if (bundle_data->child) {
1298 rsc->children = g_list_append(rsc->children, bundle_data->child);
1299 }
1300 return TRUE;
1301}
1302
1303static int
1304replica_resource_active(pcmk_resource_t *rsc, gboolean all)
1305{
1306 if (rsc) {
1307 gboolean child_active = rsc->fns->active(rsc, all);
1308
1309 if (child_active && !all) {
1310 return TRUE;
1311 } else if (!child_active && all) {
1312 return FALSE;
1313 }
1314 }
1315 return -1;
1316}
1317
1318gboolean
1320{
1321 pe__bundle_variant_data_t *bundle_data = NULL;
1322 GList *iter = NULL;
1323
1324 get_bundle_variant_data(bundle_data, rsc);
1325 for (iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
1326 pe__bundle_replica_t *replica = iter->data;
1327 int rsc_active;
1328
1329 rsc_active = replica_resource_active(replica->ip, all);
1330 if (rsc_active >= 0) {
1331 return (gboolean) rsc_active;
1332 }
1333
1334 rsc_active = replica_resource_active(replica->child, all);
1335 if (rsc_active >= 0) {
1336 return (gboolean) rsc_active;
1337 }
1338
1339 rsc_active = replica_resource_active(replica->container, all);
1340 if (rsc_active >= 0) {
1341 return (gboolean) rsc_active;
1342 }
1343
1344 rsc_active = replica_resource_active(replica->remote, all);
1345 if (rsc_active >= 0) {
1346 return (gboolean) rsc_active;
1347 }
1348 }
1349
1350 /* If "all" is TRUE, we've already checked that no resources were inactive,
1351 * so return TRUE; if "all" is FALSE, we didn't find any active resources,
1352 * so return FALSE.
1353 */
1354 return all;
1355}
1356
1368{
1369 pe__bundle_variant_data_t *bundle_data = NULL;
1370 CRM_ASSERT(bundle && node);
1371
1372 get_bundle_variant_data(bundle_data, bundle);
1373 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1374 gIter = gIter->next) {
1375 pe__bundle_replica_t *replica = gIter->data;
1376
1377 CRM_ASSERT(replica && replica->node);
1378 if (replica->node->details == node->details) {
1379 return replica->child;
1380 }
1381 }
1382 return NULL;
1383}
1384
1389static void
1390print_rsc_in_list(pcmk_resource_t *rsc, const char *pre_text, long options,
1391 void *print_data)
1392{
1393 if (rsc != NULL) {
1394 if (options & pe_print_html) {
1395 status_print("<li>");
1396 }
1397 rsc->fns->print(rsc, pre_text, options, print_data);
1398 if (options & pe_print_html) {
1399 status_print("</li>\n");
1400 }
1401 }
1402}
1403
1408static void
1409bundle_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
1410 void *print_data)
1411{
1412 pe__bundle_variant_data_t *bundle_data = NULL;
1413 char *child_text = NULL;
1414 CRM_CHECK(rsc != NULL, return);
1415
1416 if (pre_text == NULL) {
1417 pre_text = "";
1418 }
1419 child_text = crm_strdup_printf("%s ", pre_text);
1420
1421 get_bundle_variant_data(bundle_data, rsc);
1422
1423 status_print("%s<bundle ", pre_text);
1424 status_print(XML_ATTR_ID "=\"%s\" ", rsc->id);
1425 status_print("type=\"%s\" ", container_agent_str(bundle_data->agent_type));
1426 status_print("image=\"%s\" ", bundle_data->image);
1427 status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pcmk_rsc_unique));
1428 status_print("managed=\"%s\" ",
1429 pe__rsc_bool_str(rsc, pcmk_rsc_managed));
1430 status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pcmk_rsc_failed));
1431 status_print(">\n");
1432
1433 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1434 gIter = gIter->next) {
1435 pe__bundle_replica_t *replica = gIter->data;
1436
1437 CRM_ASSERT(replica);
1438 status_print("%s <replica " XML_ATTR_ID "=\"%d\">\n",
1439 pre_text, replica->offset);
1440 print_rsc_in_list(replica->ip, child_text, options, print_data);
1441 print_rsc_in_list(replica->child, child_text, options, print_data);
1442 print_rsc_in_list(replica->container, child_text, options, print_data);
1443 print_rsc_in_list(replica->remote, child_text, options, print_data);
1444 status_print("%s </replica>\n", pre_text);
1445 }
1446 status_print("%s</bundle>\n", pre_text);
1447 free(child_text);
1448}
1449
1450PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1451 "GList *")
1452int
1453pe__bundle_xml(pcmk__output_t *out, va_list args)
1454{
1455 uint32_t show_opts = va_arg(args, uint32_t);
1456 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1457 GList *only_node = va_arg(args, GList *);
1458 GList *only_rsc = va_arg(args, GList *);
1459
1460 pe__bundle_variant_data_t *bundle_data = NULL;
1461 int rc = pcmk_rc_no_output;
1462 gboolean printed_header = FALSE;
1463 gboolean print_everything = TRUE;
1464
1465 const char *desc = NULL;
1466
1467 CRM_ASSERT(rsc != NULL);
1468
1469 get_bundle_variant_data(bundle_data, rsc);
1470
1471 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1472 return rc;
1473 }
1474
1475 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1476
1477 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1478 gIter = gIter->next) {
1479 pe__bundle_replica_t *replica = gIter->data;
1480 char *id = NULL;
1481 gboolean print_ip, print_child, print_ctnr, print_remote;
1482
1483 CRM_ASSERT(replica);
1484
1485 if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1486 continue;
1487 }
1488
1489 print_ip = replica->ip != NULL &&
1490 !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1491 print_child = replica->child != NULL &&
1492 !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1493 print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1494 print_remote = replica->remote != NULL &&
1495 !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1496
1497 if (!print_everything && !print_ip && !print_child && !print_ctnr && !print_remote) {
1498 continue;
1499 }
1500
1501 if (!printed_header) {
1502 printed_header = TRUE;
1503
1504 desc = pe__resource_description(rsc, show_opts);
1505
1506 rc = pe__name_and_nvpairs_xml(out, true, "bundle", 8,
1507 "id", rsc->id,
1508 "type", container_agent_str(bundle_data->agent_type),
1509 "image", bundle_data->image,
1510 "unique", pe__rsc_bool_str(rsc, pcmk_rsc_unique),
1511 "maintenance",
1512 pe__rsc_bool_str(rsc, pcmk_rsc_maintenance),
1513 "managed", pe__rsc_bool_str(rsc, pcmk_rsc_managed),
1514 "failed", pe__rsc_bool_str(rsc, pcmk_rsc_failed),
1515 "description", desc);
1516 CRM_ASSERT(rc == pcmk_rc_ok);
1517 }
1518
1519 id = pcmk__itoa(replica->offset);
1520 rc = pe__name_and_nvpairs_xml(out, true, "replica", 1, "id", id);
1521 free(id);
1522 CRM_ASSERT(rc == pcmk_rc_ok);
1523
1524 if (print_ip) {
1525 out->message(out, crm_map_element_name(replica->ip->xml), show_opts,
1526 replica->ip, only_node, only_rsc);
1527 }
1528
1529 if (print_child) {
1530 out->message(out, crm_map_element_name(replica->child->xml), show_opts,
1531 replica->child, only_node, only_rsc);
1532 }
1533
1534 if (print_ctnr) {
1535 out->message(out, crm_map_element_name(replica->container->xml), show_opts,
1536 replica->container, only_node, only_rsc);
1537 }
1538
1539 if (print_remote) {
1540 out->message(out, crm_map_element_name(replica->remote->xml), show_opts,
1541 replica->remote, only_node, only_rsc);
1542 }
1543
1544 pcmk__output_xml_pop_parent(out); // replica
1545 }
1546
1547 if (printed_header) {
1548 pcmk__output_xml_pop_parent(out); // bundle
1549 }
1550
1551 return rc;
1552}
1553
1554static void
1555pe__bundle_replica_output_html(pcmk__output_t *out, pe__bundle_replica_t *replica,
1556 pcmk_node_t *node, uint32_t show_opts)
1557{
1558 pcmk_resource_t *rsc = replica->child;
1559
1560 int offset = 0;
1561 char buffer[LINE_MAX];
1562
1563 if(rsc == NULL) {
1564 rsc = replica->container;
1565 }
1566
1567 if (replica->remote) {
1568 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1569 rsc_printable_id(replica->remote));
1570 } else {
1571 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1572 rsc_printable_id(replica->container));
1573 }
1574 if (replica->ipaddr) {
1575 offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1576 replica->ipaddr);
1577 }
1578
1579 pe__common_output_html(out, rsc, buffer, node, show_opts);
1580}
1581
1591static const char *
1592get_unmanaged_str(const pcmk_resource_t *rsc)
1593{
1595 return " (maintenance)";
1596 }
1597 if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
1598 return " (unmanaged)";
1599 }
1600 return "";
1601}
1602
1603PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1604 "GList *")
1605int
1606pe__bundle_html(pcmk__output_t *out, va_list args)
1607{
1608 uint32_t show_opts = va_arg(args, uint32_t);
1609 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1610 GList *only_node = va_arg(args, GList *);
1611 GList *only_rsc = va_arg(args, GList *);
1612
1613 const char *desc = NULL;
1614 pe__bundle_variant_data_t *bundle_data = NULL;
1615 int rc = pcmk_rc_no_output;
1616 gboolean print_everything = TRUE;
1617
1618 CRM_ASSERT(rsc != NULL);
1619
1620 get_bundle_variant_data(bundle_data, rsc);
1621
1622 desc = pe__resource_description(rsc, show_opts);
1623
1624 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1625 return rc;
1626 }
1627
1628 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1629
1630 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1631 gIter = gIter->next) {
1632 pe__bundle_replica_t *replica = gIter->data;
1633 gboolean print_ip, print_child, print_ctnr, print_remote;
1634
1635 CRM_ASSERT(replica);
1636
1637 if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1638 continue;
1639 }
1640
1641 print_ip = replica->ip != NULL &&
1642 !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1643 print_child = replica->child != NULL &&
1644 !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1645 print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1646 print_remote = replica->remote != NULL &&
1647 !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1648
1649 if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
1650 (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
1651 /* The text output messages used below require pe_print_implicit to
1652 * be set to do anything.
1653 */
1654 uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
1655
1656 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1657 (bundle_data->nreplicas > 1)? " set" : "",
1658 rsc->id, bundle_data->image,
1659 pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1660 desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1661 get_unmanaged_str(rsc));
1662
1663 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1664 out->begin_list(out, NULL, NULL, "Replica[%d]", replica->offset);
1665 }
1666
1667 if (print_ip) {
1668 out->message(out, crm_map_element_name(replica->ip->xml),
1669 new_show_opts, replica->ip, only_node, only_rsc);
1670 }
1671
1672 if (print_child) {
1673 out->message(out, crm_map_element_name(replica->child->xml),
1674 new_show_opts, replica->child, only_node, only_rsc);
1675 }
1676
1677 if (print_ctnr) {
1678 out->message(out, crm_map_element_name(replica->container->xml),
1679 new_show_opts, replica->container, only_node, only_rsc);
1680 }
1681
1682 if (print_remote) {
1683 out->message(out, crm_map_element_name(replica->remote->xml),
1684 new_show_opts, replica->remote, only_node, only_rsc);
1685 }
1686
1687 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1688 out->end_list(out);
1689 }
1690 } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
1691 continue;
1692 } else {
1693 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1694 (bundle_data->nreplicas > 1)? " set" : "",
1695 rsc->id, bundle_data->image,
1696 pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1697 desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1698 get_unmanaged_str(rsc));
1699
1700 pe__bundle_replica_output_html(out, replica, pe__current_node(replica->container),
1701 show_opts);
1702 }
1703 }
1704
1705 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1706 return rc;
1707}
1708
1709static void
1710pe__bundle_replica_output_text(pcmk__output_t *out, pe__bundle_replica_t *replica,
1711 pcmk_node_t *node, uint32_t show_opts)
1712{
1713 const pcmk_resource_t *rsc = replica->child;
1714
1715 int offset = 0;
1716 char buffer[LINE_MAX];
1717
1718 if(rsc == NULL) {
1719 rsc = replica->container;
1720 }
1721
1722 if (replica->remote) {
1723 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1724 rsc_printable_id(replica->remote));
1725 } else {
1726 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1727 rsc_printable_id(replica->container));
1728 }
1729 if (replica->ipaddr) {
1730 offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1731 replica->ipaddr);
1732 }
1733
1734 pe__common_output_text(out, rsc, buffer, node, show_opts);
1735}
1736
1737PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1738 "GList *")
1739int
1740pe__bundle_text(pcmk__output_t *out, va_list args)
1741{
1742 uint32_t show_opts = va_arg(args, uint32_t);
1743 pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1744 GList *only_node = va_arg(args, GList *);
1745 GList *only_rsc = va_arg(args, GList *);
1746
1747 const char *desc = NULL;
1748 pe__bundle_variant_data_t *bundle_data = NULL;
1749 int rc = pcmk_rc_no_output;
1750 gboolean print_everything = TRUE;
1751
1752 desc = pe__resource_description(rsc, show_opts);
1753
1754 get_bundle_variant_data(bundle_data, rsc);
1755
1756 CRM_ASSERT(rsc != NULL);
1757
1758 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1759 return rc;
1760 }
1761
1762 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1763
1764 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1765 gIter = gIter->next) {
1766 pe__bundle_replica_t *replica = gIter->data;
1767 gboolean print_ip, print_child, print_ctnr, print_remote;
1768
1769 CRM_ASSERT(replica);
1770
1771 if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1772 continue;
1773 }
1774
1775 print_ip = replica->ip != NULL &&
1776 !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1777 print_child = replica->child != NULL &&
1778 !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1779 print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1780 print_remote = replica->remote != NULL &&
1781 !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1782
1783 if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
1784 (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
1785 /* The text output messages used below require pe_print_implicit to
1786 * be set to do anything.
1787 */
1788 uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
1789
1790 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1791 (bundle_data->nreplicas > 1)? " set" : "",
1792 rsc->id, bundle_data->image,
1793 pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1794 desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1795 get_unmanaged_str(rsc));
1796
1797 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1798 out->list_item(out, NULL, "Replica[%d]", replica->offset);
1799 }
1800
1801 out->begin_list(out, NULL, NULL, NULL);
1802
1803 if (print_ip) {
1804 out->message(out, crm_map_element_name(replica->ip->xml),
1805 new_show_opts, replica->ip, only_node, only_rsc);
1806 }
1807
1808 if (print_child) {
1809 out->message(out, crm_map_element_name(replica->child->xml),
1810 new_show_opts, replica->child, only_node, only_rsc);
1811 }
1812
1813 if (print_ctnr) {
1814 out->message(out, crm_map_element_name(replica->container->xml),
1815 new_show_opts, replica->container, only_node, only_rsc);
1816 }
1817
1818 if (print_remote) {
1819 out->message(out, crm_map_element_name(replica->remote->xml),
1820 new_show_opts, replica->remote, only_node, only_rsc);
1821 }
1822
1823 out->end_list(out);
1824 } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
1825 continue;
1826 } else {
1827 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1828 (bundle_data->nreplicas > 1)? " set" : "",
1829 rsc->id, bundle_data->image,
1830 pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1831 desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1832 get_unmanaged_str(rsc));
1833
1834 pe__bundle_replica_output_text(out, replica, pe__current_node(replica->container),
1835 show_opts);
1836 }
1837 }
1838
1839 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1840 return rc;
1841}
1842
1847static void
1848print_bundle_replica(pe__bundle_replica_t *replica, const char *pre_text,
1849 long options, void *print_data)
1850{
1851 pcmk_node_t *node = NULL;
1852 pcmk_resource_t *rsc = replica->child;
1853
1854 int offset = 0;
1855 char buffer[LINE_MAX];
1856
1857 if(rsc == NULL) {
1858 rsc = replica->container;
1859 }
1860
1861 if (replica->remote) {
1862 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1863 rsc_printable_id(replica->remote));
1864 } else {
1865 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1866 rsc_printable_id(replica->container));
1867 }
1868 if (replica->ipaddr) {
1869 offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1870 replica->ipaddr);
1871 }
1872
1873 node = pe__current_node(replica->container);
1874 common_print(rsc, pre_text, buffer, node, options, print_data);
1875}
1876
1881void
1882pe__print_bundle(pcmk_resource_t *rsc, const char *pre_text, long options,
1883 void *print_data)
1884{
1885 pe__bundle_variant_data_t *bundle_data = NULL;
1886 char *child_text = NULL;
1887 CRM_CHECK(rsc != NULL, return);
1888
1889 if (options & pe_print_xml) {
1890 bundle_print_xml(rsc, pre_text, options, print_data);
1891 return;
1892 }
1893
1894 get_bundle_variant_data(bundle_data, rsc);
1895
1896 if (pre_text == NULL) {
1897 pre_text = " ";
1898 }
1899
1900 status_print("%sContainer bundle%s: %s [%s]%s%s\n",
1901 pre_text, ((bundle_data->nreplicas > 1)? " set" : ""),
1902 rsc->id, bundle_data->image,
1903 pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1904 pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " (unmanaged)");
1905 if (options & pe_print_html) {
1906 status_print("<br />\n<ul>\n");
1907 }
1908
1909
1910 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1911 gIter = gIter->next) {
1912 pe__bundle_replica_t *replica = gIter->data;
1913
1914 CRM_ASSERT(replica);
1915 if (options & pe_print_html) {
1916 status_print("<li>");
1917 }
1918
1919 if (pcmk_is_set(options, pe_print_implicit)) {
1920 child_text = crm_strdup_printf(" %s", pre_text);
1921 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1922 status_print(" %sReplica[%d]\n", pre_text, replica->offset);
1923 }
1924 if (options & pe_print_html) {
1925 status_print("<br />\n<ul>\n");
1926 }
1927 print_rsc_in_list(replica->ip, child_text, options, print_data);
1928 print_rsc_in_list(replica->container, child_text, options, print_data);
1929 print_rsc_in_list(replica->remote, child_text, options, print_data);
1930 print_rsc_in_list(replica->child, child_text, options, print_data);
1931 if (options & pe_print_html) {
1932 status_print("</ul>\n");
1933 }
1934 } else {
1935 child_text = crm_strdup_printf("%s ", pre_text);
1936 print_bundle_replica(replica, child_text, options, print_data);
1937 }
1938 free(child_text);
1939
1940 if (options & pe_print_html) {
1941 status_print("</li>\n");
1942 }
1943 }
1944 if (options & pe_print_html) {
1945 status_print("</ul>\n");
1946 }
1947}
1948
1949static void
1950free_bundle_replica(pe__bundle_replica_t *replica)
1951{
1952 if (replica == NULL) {
1953 return;
1954 }
1955
1956 if (replica->node) {
1957 free(replica->node);
1958 replica->node = NULL;
1959 }
1960
1961 if (replica->ip) {
1962 free_xml(replica->ip->xml);
1963 replica->ip->xml = NULL;
1964 replica->ip->fns->free(replica->ip);
1965 replica->ip = NULL;
1966 }
1967 if (replica->container) {
1968 free_xml(replica->container->xml);
1969 replica->container->xml = NULL;
1970 replica->container->fns->free(replica->container);
1971 replica->container = NULL;
1972 }
1973 if (replica->remote) {
1974 free_xml(replica->remote->xml);
1975 replica->remote->xml = NULL;
1976 replica->remote->fns->free(replica->remote);
1977 replica->remote = NULL;
1978 }
1979 free(replica->ipaddr);
1980 free(replica);
1981}
1982
1983void
1985{
1986 pe__bundle_variant_data_t *bundle_data = NULL;
1987 CRM_CHECK(rsc != NULL, return);
1988
1989 get_bundle_variant_data(bundle_data, rsc);
1990 pe_rsc_trace(rsc, "Freeing %s", rsc->id);
1991
1992 free(bundle_data->prefix);
1993 free(bundle_data->image);
1994 free(bundle_data->control_port);
1995 free(bundle_data->host_network);
1996 free(bundle_data->host_netmask);
1997 free(bundle_data->ip_range_start);
1998 free(bundle_data->container_network);
1999 free(bundle_data->launcher_options);
2000 free(bundle_data->container_command);
2001 g_free(bundle_data->container_host_options);
2002
2003 g_list_free_full(bundle_data->replicas,
2004 (GDestroyNotify) free_bundle_replica);
2005 g_list_free_full(bundle_data->mounts, (GDestroyNotify)mount_free);
2006 g_list_free_full(bundle_data->ports, (GDestroyNotify)port_free);
2007 g_list_free(rsc->children);
2008
2009 if(bundle_data->child) {
2010 free_xml(bundle_data->child->xml);
2011 bundle_data->child->xml = NULL;
2012 bundle_data->child->fns->free(bundle_data->child);
2013 }
2014 common_free(rsc);
2015}
2016
2017enum rsc_role_e
2018pe__bundle_resource_state(const pcmk_resource_t *rsc, gboolean current)
2019{
2020 enum rsc_role_e container_role = pcmk_role_unknown;
2021 return container_role;
2022}
2023
2031int
2033{
2034 if ((rsc == NULL) || (rsc->variant != pcmk_rsc_variant_bundle)) {
2035 return 0;
2036 } else {
2037 pe__bundle_variant_data_t *bundle_data = NULL;
2038
2039 get_bundle_variant_data(bundle_data, rsc);
2040 return bundle_data->nreplicas;
2041 }
2042}
2043
2044void
2046{
2047 pe__bundle_variant_data_t *bundle_data = NULL;
2048
2049 get_bundle_variant_data(bundle_data, rsc);
2050 for (GList *item = bundle_data->replicas; item != NULL; item = item->next) {
2051 pe__bundle_replica_t *replica = item->data;
2052
2053 if (replica->ip) {
2054 replica->ip->fns->count(replica->ip);
2055 }
2056 if (replica->child) {
2057 replica->child->fns->count(replica->child);
2058 }
2059 if (replica->container) {
2060 replica->container->fns->count(replica->container);
2061 }
2062 if (replica->remote) {
2063 replica->remote->fns->count(replica->remote);
2064 }
2065 }
2066}
2067
2068gboolean
2069pe__bundle_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
2070 gboolean check_parent)
2071{
2072 gboolean passes = FALSE;
2073 pe__bundle_variant_data_t *bundle_data = NULL;
2074
2076 passes = TRUE;
2077 } else {
2078 get_bundle_variant_data(bundle_data, rsc);
2079
2080 for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) {
2081 pe__bundle_replica_t *replica = gIter->data;
2082
2083 if (replica->ip != NULL && !replica->ip->fns->is_filtered(replica->ip, only_rsc, FALSE)) {
2084 passes = TRUE;
2085 break;
2086 } else if (replica->child != NULL && !replica->child->fns->is_filtered(replica->child, only_rsc, FALSE)) {
2087 passes = TRUE;
2088 break;
2089 } else if (!replica->container->fns->is_filtered(replica->container, only_rsc, FALSE)) {
2090 passes = TRUE;
2091 break;
2092 } else if (replica->remote != NULL && !replica->remote->fns->is_filtered(replica->remote, only_rsc, FALSE)) {
2093 passes = TRUE;
2094 break;
2095 }
2096 }
2097 }
2098
2099 return !passes;
2100}
2101
2112GList *
2114{
2115 GList *containers = NULL;
2116 const pe__bundle_variant_data_t *data = NULL;
2117
2119 for (GList *iter = data->replicas; iter != NULL; iter = iter->next) {
2120 pe__bundle_replica_t *replica = iter->data;
2121
2122 containers = g_list_append(containers, replica->container);
2123 }
2124 return containers;
2125}
2126
2127// Bundle implementation of pcmk_rsc_methods_t:active_node()
2129pe__bundle_active_node(const pcmk_resource_t *rsc, unsigned int *count_all,
2130 unsigned int *count_clean)
2131{
2132 pcmk_node_t *active = NULL;
2133 pcmk_node_t *node = NULL;
2134 pcmk_resource_t *container = NULL;
2135 GList *containers = NULL;
2136 GList *iter = NULL;
2137 GHashTable *nodes = NULL;
2138 const pe__bundle_variant_data_t *data = NULL;
2139
2140 if (count_all != NULL) {
2141 *count_all = 0;
2142 }
2143 if (count_clean != NULL) {
2144 *count_clean = 0;
2145 }
2146 if (rsc == NULL) {
2147 return NULL;
2148 }
2149
2150 /* For the purposes of this method, we only care about where the bundle's
2151 * containers are active, so build a list of active containers.
2152 */
2154 for (iter = data->replicas; iter != NULL; iter = iter->next) {
2155 pe__bundle_replica_t *replica = iter->data;
2156
2157 if (replica->container->running_on != NULL) {
2158 containers = g_list_append(containers, replica->container);
2159 }
2160 }
2161 if (containers == NULL) {
2162 return NULL;
2163 }
2164
2165 /* If the bundle has only a single active container, just use that
2166 * container's method. If live migration is ever supported for bundle
2167 * containers, this will allow us to prefer the migration source when there
2168 * is only one container and it is migrating. For now, this just lets us
2169 * avoid creating the nodes table.
2170 */
2171 if (pcmk__list_of_1(containers)) {
2172 container = containers->data;
2173 node = container->fns->active_node(container, count_all, count_clean);
2174 g_list_free(containers);
2175 return node;
2176 }
2177
2178 // Add all containers' active nodes to a hash table (for uniqueness)
2179 nodes = g_hash_table_new(NULL, NULL);
2180 for (iter = containers; iter != NULL; iter = iter->next) {
2181 container = iter->data;
2182
2183 for (GList *node_iter = container->running_on; node_iter != NULL;
2184 node_iter = node_iter->next) {
2185 node = node_iter->data;
2186
2187 // If insert returns true, we haven't counted this node yet
2188 if (g_hash_table_insert(nodes, (gpointer) node->details,
2189 (gpointer) node)
2190 && !pe__count_active_node(rsc, node, &active, count_all,
2191 count_clean)) {
2192 goto done;
2193 }
2194 }
2195 }
2196
2197done:
2198 g_list_free(containers);
2199 g_hash_table_destroy(nodes);
2200 return active;
2201}
2202
2211unsigned int
2213{
2214 pe__bundle_variant_data_t *bundle_data = NULL;
2215
2216 get_bundle_variant_data(bundle_data, rsc);
2217 CRM_ASSERT(bundle_data->nreplicas_per_host >= 0);
2218 return (unsigned int) bundle_data->nreplicas_per_host;
2219}
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:428
#define PCMK_ACTION_MONITOR
Definition actions.h:59
#define PCMK_RESOURCE_CLASS_OCF
Definition agents.h:27
pe__container_agent
Definition bundle.c:42
@ PE__CONTAINER_AGENT_PODMAN
Definition bundle.c:46
@ PE__CONTAINER_AGENT_UNKNOWN
Definition bundle.c:43
@ PE__CONTAINER_AGENT_DOCKER
Definition bundle.c:44
@ PE__CONTAINER_AGENT_RKT
Definition bundle.c:45
enum rsc_role_e pe__bundle_resource_state(const pcmk_resource_t *rsc, gboolean current)
Definition bundle.c:2018
struct pe__bundle_variant_data_s pe__bundle_variant_data_t
gboolean pe__bundle_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition bundle.c:2069
void pe__count_bundle(pcmk_resource_t *rsc)
Definition bundle.c:2045
void pe__foreach_bundle_replica(pcmk_resource_t *bundle, bool(*fn)(pe__bundle_replica_t *, void *), void *user_data)
Definition bundle.c:210
#define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set)
Definition bundle.c:977
pcmk_resource_t * pe__find_bundle_replica(const pcmk_resource_t *bundle, const pcmk_node_t *node)
Definition bundle.c:1367
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:2032
#define PE__CONTAINER_AGENT_UNKNOWN_S
Definition bundle.c:49
unsigned int pe__bundle_max_per_node(const pcmk_resource_t *rsc)
Definition bundle.c:2212
pcmk_node_t * pe__bundle_active_node(const pcmk_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean)
Definition bundle.c:2129
pcmk_resource_t * pe__first_container(const pcmk_resource_t *bundle)
Definition bundle.c:187
void pe__foreach_const_bundle_replica(const pcmk_resource_t *bundle, bool(*fn)(const pe__bundle_replica_t *, void *), void *user_data)
Definition bundle.c:234
gboolean pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
Definition bundle.c:984
bool pe__bundle_needs_remote_name(pcmk_resource_t *rsc)
Definition bundle.c:920
#define PE__CONTAINER_AGENT_PODMAN_S
Definition bundle.c:52
#define PE__CONTAINER_AGENT_RKT_S
Definition bundle.c:51
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:2113
pcmk_resource_t * pe__bundled_resource(const pcmk_resource_t *rsc)
Definition bundle.c:113
void pe__print_bundle(pcmk_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition bundle.c:1882
gboolean pe__bundle_active(pcmk_resource_t *rsc, gboolean all)
Definition bundle.c:1319
#define get_bundle_variant_data(data, rsc)
Definition bundle.c:81
#define PE__CONTAINER_AGENT_DOCKER_S
Definition bundle.c:50
bool pe__node_is_bundle_instance(const pcmk_resource_t *bundle, const pcmk_node_t *node)
Definition bundle.c:161
const char * pe__add_bundle_remote_name(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, xmlNode *xml, const char *field)
Definition bundle.c:938
void pe__free_bundle(pcmk_resource_t *rsc)
Definition bundle.c:1984
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
uint64_t flags
Definition remote.c:3
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:424
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:99
int pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, pcmk_resource_t *parent, pcmk_scheduler_t *scheduler)
Definition complex.c:603
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:446
#define SBIN_DIR
Definition config.h:574
#define CRM_BUNDLE_DIR
Definition config.h:14
#define CRM_DAEMON_DIR
Definition config.h:24
char uname[MAX_NAME]
Definition cpg.c:5
char data[0]
Definition cpg.c:10
uint32_t id
Definition cpg.c:0
#define INFINITY
Definition crm.h:98
#define CRM_ATTR_KIND
Definition crm.h:115
#define CRM_LOG_ASSERT(expr)
Definition logging.h:222
#define CRM_CHECK(expr, failure_action)
Definition logging.h:238
#define crm_trace(fmt, args...)
Definition logging.h:385
#define DEFAULT_REMOTE_KEY_LOCATION
Definition lrmd.h:50
#define DEFAULT_REMOTE_PORT
Definition lrmd.h:52
#define ID(x)
Definition msg_xml.h:474
#define XML_BOOLEAN_TRUE
Definition msg_xml.h:167
#define XML_RSC_ATTR_TARGET
Definition msg_xml.h:242
#define PCMK_META_CLONE_NODE_MAX
Definition msg_xml.h:66
#define XML_TAG_ATTR_SETS
Definition msg_xml.h:227
#define XML_CIB_TAG_INCARNATION
Definition msg_xml.h:237
#define XML_ATTR_ID
Definition msg_xml.h:156
#define PCMK_META_PROMOTED_MAX
Definition msg_xml.h:70
#define XML_AGENT_ATTR_PROVIDER
Definition msg_xml.h:281
#define PCMK_META_CLONE_MAX
Definition msg_xml.h:64
#define XML_AGENT_ATTR_CLASS
Definition msg_xml.h:280
#define XML_TAG_META_SETS
Definition msg_xml.h:228
#define XML_BOOLEAN_FALSE
Definition msg_xml.h:168
#define XML_ATTR_TYPE
Definition msg_xml.h:160
#define XML_RSC_ATTR_ORDERED
Definition msg_xml.h:244
#define XML_RSC_ATTR_UNIQUE
Definition msg_xml.h:250
#define XML_RSC_ATTR_REMOTE_RA_ADDR
Definition msg_xml.h:261
#define XML_CIB_TAG_RESOURCE
Definition msg_xml.h:235
#define XML_RSC_ATTR_PROMOTABLE
Definition msg_xml.h:247
pcmk_scheduler_t * scheduler
@ pcmk_probe_never
Always probe resource on node.
Definition nodes.h:50
@ pcmk_probe_exclusive
Never probe resource on node.
Definition nodes.h:51
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:447
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:763
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition nvpair.c:644
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition nvpair.c:302
#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:522
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
#define PCMK__OUTPUT_ARGS(ARGS...)
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
const char * target
Definition pcmk_fence.c:29
#define status_print(fmt, args...)
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition utils.c:89
int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name, size_t pairs_count,...)
Definition pe_output.c:597
pcmk_node_t * pe_create_node(const char *id, const char *uname, const char *type, const char *score, pcmk_scheduler_t *scheduler)
Definition unpack.c:440
const char * pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts)
Definition pe_output.c:22
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition complex.c:962
void common_print(pcmk_resource_t *rsc, const char *pre_text, const char *name, const pcmk_node_t *node, long options, void *print_data)
Definition native.c:811
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)
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition internal.h:70
int pe__bundle_text(pcmk__output_t *out, va_list args)
#define pe_rsc_trace(rsc, fmt, args...)
Definition internal.h:37
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:1058
#define pe__set_resource_flags(resource, flags_to_set)
Definition internal.h:64
void common_free(pcmk_resource_t *rsc)
Definition complex.c:980
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:775
#define pe_err(fmt...)
Definition internal.h:39
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition common.c:508
bool xml_contains_remote_node(xmlNode *xml)
Definition remote.c:84
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:160
bool pe__is_guest_or_remote_node(const pcmk_node_t *node)
Definition remote.c:41
@ pe_print_implicit
Definition resources.h:257
@ pe_print_xml
Definition resources.h:252
@ pe_print_html
Definition resources.h:243
@ pcmk_rsc_variant_bundle
Bundle resource.
Definition resources.h:37
@ pcmk_rsc_unique
Whether resource is not an anonymous clone instance.
Definition resources.h:118
@ pcmk_rsc_maintenance
Whether resource, its node, or entire cluster is in maintenance mode.
Definition resources.h:181
@ pcmk_rsc_replica_container
Whether resource is an implicit container resource for a bundle replica.
Definition resources.h:178
@ pcmk_rsc_notify
Whether resource has clone notifications enabled.
Definition resources.h:115
@ pcmk_rsc_remote_nesting_allowed
Whether resource is a remote connection allowed to run on a remote node.
Definition resources.h:145
@ pcmk_rsc_managed
Whether resource is managed.
Definition resources.h:106
@ pcmk_rsc_failed
Whether resource is considered failed.
Definition resources.h:151
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_no_output
Definition results.h:124
@ pcmk_rc_ok
Definition results.h:154
@ pcmk_rc_unpack_error
Definition results.h:118
rsc_role_e
Definition roles.h:27
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:28
Cluster status and scheduling.
pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id_rh)
Definition status.c:391
pcmk_node_t * pe_find_node(const GList *node_list, const char *node_name)
Find a node by name in a list of nodes.
Definition status.c:473
const char * rsc_printable_id(const pcmk_resource_t *rsc)
Definition utils.c:546
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:127
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition strings.c:888
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:608
void pcmk__str_update(char **str, const char *value)
Definition strings.c:1193
@ pcmk__str_none
@ pcmk__str_star_matches
@ pcmk__str_casei
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1217
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition strings.c:700
This structure contains everything that makes up a single output formatter.
A single instance of a bundle.
Definition internal.h:519
char * ipaddr
IP address associated with this instance.
Definition internal.h:521
pcmk_node_t * node
Node created for this instance.
Definition internal.h:522
pcmk_resource_t * remote
Pacemaker Remote connection into container.
Definition internal.h:526
pcmk_resource_t * container
Container associated with this instance.
Definition internal.h:525
int offset
0-origin index of this instance in bundle
Definition internal.h:520
pcmk_resource_t * child
Instance of bundled resource.
Definition internal.h:524
pcmk_resource_t * ip
IP address resource for ipaddr.
Definition internal.h:523
Implementation of pcmk_node_t.
Definition nodes.h:130
int weight
Node score for a given resource.
Definition nodes.h:131
int rsc_discover_mode
Probe mode (enum pe_discover_e)
Definition nodes.h:137
struct pe_node_shared_s * details
Basic node information.
Definition nodes.h:134
GHashTable * attrs
Node attributes.
Definition nodes.h:115
const char * id
Node ID at the cluster layer.
Definition nodes.h:67
const char * uname
Node name in cluster.
Definition nodes.h:68
pcmk_resource_t * remote_rsc
Remote connection resource for node, if it is a Pacemaker Remote node.
Definition nodes.h:111
Implementation of pcmk_resource_t.
Definition resources.h:399
GList * running_on
Nodes where resource may be active.
Definition resources.h:460
enum pe_obj_types variant
Resource variant.
Definition resources.h:414
GHashTable * meta
Resource's meta-attributes.
Definition resources.h:471
GList * children
Resource's child resources, if any.
Definition resources.h:475
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition resources.h:412
gboolean exclusive_discover
Whether exclusive probing is enabled.
Definition resources.h:433
pcmk_resource_t * container
Resource containing this one, if any.
Definition resources.h:480
pcmk_rsc_methods_t * fns
Resource object methods.
Definition resources.h:416
char * id
Resource ID in configuration.
Definition resources.h:400
pcmk_node_t * allocated_to
Node resource is assigned to.
Definition resources.h:451
xmlNode * xml
Resource configuration (possibly expanded from template)
Definition resources.h:404
GHashTable * utilization
Resource's utilization attributes.
Definition resources.h:473
GHashTable * allowed_nodes
Nodes where resource may run (key is node ID, not name)
Definition resources.h:466
void * variant_opaque
Variant-specific (and private) data.
Definition resources.h:415
unsigned long long flags
Group of enum pcmk_rsc_flags.
Definition resources.h:429
pcmk_resource_t * parent
Resource's parent resource, if any.
Definition resources.h:413
Implementation of pcmk_scheduler_t.
Definition scheduler.h:172
void(* free)(pcmk_resource_t *rsc)
Free all memory used by a resource.
Definition resources.h:347
void(* print)(pcmk_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition resources.h:306
void(* count)(pcmk_resource_t *rsc)
Increment cluster's instance counts for a resource.
Definition resources.h:357
pcmk_node_t *(* active_node)(const pcmk_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean)
Find a node (and optionally count all) where resource is active.
Definition resources.h:384
gboolean(* is_filtered)(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Check whether a given resource is in a list of resources.
Definition resources.h:369
gboolean(* active)(pcmk_resource_t *rsc, gboolean all)
Check whether a resource is active.
Definition resources.h:317
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition xml.c:2484
void crm_xml_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
void free_xml(xmlNode *child)
Definition xml.c:783
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition xml.c:1106
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition xml.c:622
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition xml.c:638