pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
bundle.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU 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
23#define PE__VARIANT_BUNDLE 1
24#include "./variant.h"
25
26static char *
27next_ip(const char *last_ip)
28{
29 unsigned int oct1 = 0;
30 unsigned int oct2 = 0;
31 unsigned int oct3 = 0;
32 unsigned int oct4 = 0;
33 int rc = sscanf(last_ip, "%u.%u.%u.%u", &oct1, &oct2, &oct3, &oct4);
34
35 if (rc != 4) {
36 /*@ TODO check for IPv6 */
37 return NULL;
38
39 } else if (oct3 > 253) {
40 return NULL;
41
42 } else if (oct4 > 253) {
43 ++oct3;
44 oct4 = 1;
45
46 } else {
47 ++oct4;
48 }
49
50 return crm_strdup_printf("%u.%u.%u.%u", oct1, oct2, oct3, oct4);
51}
52
53static void
54allocate_ip(pe__bundle_variant_data_t *data, pe__bundle_replica_t *replica,
55 GString *buffer)
56{
57 if(data->ip_range_start == NULL) {
58 return;
59
60 } else if(data->ip_last) {
61 replica->ipaddr = next_ip(data->ip_last);
62
63 } else {
64 replica->ipaddr = strdup(data->ip_range_start);
65 }
66
67 data->ip_last = replica->ipaddr;
68 switch (data->agent_type) {
69 case PE__CONTAINER_AGENT_DOCKER:
70 case PE__CONTAINER_AGENT_PODMAN:
71 if (data->add_host) {
72 g_string_append_printf(buffer, " --add-host=%s-%d:%s",
73 data->prefix, replica->offset,
74 replica->ipaddr);
75 } else {
76 g_string_append_printf(buffer, " --hosts-entry=%s=%s-%d",
77 replica->ipaddr, data->prefix,
78 replica->offset);
79 }
80 break;
81
82 case PE__CONTAINER_AGENT_RKT:
83 g_string_append_printf(buffer, " --hosts-entry=%s=%s-%d",
84 replica->ipaddr, data->prefix,
85 replica->offset);
86 break;
87
88 default: // PE__CONTAINER_AGENT_UNKNOWN
89 break;
90 }
91}
92
93static xmlNode *
94create_resource(const char *name, const char *provider, const char *kind)
95{
96 xmlNode *rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE);
97
100 crm_xml_add(rsc, XML_AGENT_ATTR_PROVIDER, provider);
101 crm_xml_add(rsc, XML_ATTR_TYPE, kind);
102
103 return rsc;
104}
105
118static bool
119valid_network(pe__bundle_variant_data_t *data)
120{
121 if(data->ip_range_start) {
122 return TRUE;
123 }
124 if(data->control_port) {
125 if(data->nreplicas_per_host > 1) {
126 pe_err("Specifying the 'control-port' for %s requires 'replicas-per-host=1'", data->prefix);
127 data->nreplicas_per_host = 1;
128 // @TODO to be sure: pe__clear_resource_flags(rsc, pe_rsc_unique);
129 }
130 return TRUE;
131 }
132 return FALSE;
133}
134
135static int
136create_ip_resource(pe_resource_t *parent, pe__bundle_variant_data_t *data,
137 pe__bundle_replica_t *replica)
138{
139 if(data->ip_range_start) {
140 char *id = NULL;
141 xmlNode *xml_ip = NULL;
142 xmlNode *xml_obj = NULL;
143
144 id = crm_strdup_printf("%s-ip-%s", data->prefix, replica->ipaddr);
146 xml_ip = create_resource(id, "heartbeat", "IPaddr2");
147 free(id);
148
149 xml_obj = create_xml_node(xml_ip, XML_TAG_ATTR_SETS);
150 crm_xml_set_id(xml_obj, "%s-attributes-%d",
151 data->prefix, replica->offset);
152
153 crm_create_nvpair_xml(xml_obj, NULL, "ip", replica->ipaddr);
154 if(data->host_network) {
155 crm_create_nvpair_xml(xml_obj, NULL, "nic", data->host_network);
156 }
157
158 if(data->host_netmask) {
159 crm_create_nvpair_xml(xml_obj, NULL,
160 "cidr_netmask", data->host_netmask);
161
162 } else {
163 crm_create_nvpair_xml(xml_obj, NULL, "cidr_netmask", "32");
164 }
165
166 xml_obj = create_xml_node(xml_ip, "operations");
167 crm_create_op_xml(xml_obj, ID(xml_ip), "monitor", "60s", NULL);
168
169 // TODO: Other ops? Timeouts and intervals from underlying resource?
170
171 if (pe__unpack_resource(xml_ip, &replica->ip, parent,
172 parent->cluster) != pcmk_rc_ok) {
174 }
175
176 parent->children = g_list_append(parent->children, replica->ip);
177 }
178 return pcmk_rc_ok;
179}
180
181static const char*
182container_agent_str(enum pe__container_agent t)
183{
184 switch (t) {
185 case PE__CONTAINER_AGENT_DOCKER: return PE__CONTAINER_AGENT_DOCKER_S;
186 case PE__CONTAINER_AGENT_RKT: return PE__CONTAINER_AGENT_RKT_S;
187 case PE__CONTAINER_AGENT_PODMAN: return PE__CONTAINER_AGENT_PODMAN_S;
188 default: // PE__CONTAINER_AGENT_UNKNOWN
189 break;
190 }
191 return PE__CONTAINER_AGENT_UNKNOWN_S;
192}
193
194static int
195create_container_resource(pe_resource_t *parent,
196 const pe__bundle_variant_data_t *data,
197 pe__bundle_replica_t *replica)
198{
199 char *id = NULL;
200 xmlNode *xml_container = NULL;
201 xmlNode *xml_obj = NULL;
202
203 // Agent-specific
204 const char *hostname_opt = NULL;
205 const char *env_opt = NULL;
206 const char *agent_str = NULL;
207 int volid = 0; // rkt-only
208
209 GString *buffer = NULL;
210 GString *dbuffer = NULL;
211
212 // Where syntax differences are drop-in replacements, set them now
213 switch (data->agent_type) {
214 case PE__CONTAINER_AGENT_DOCKER:
215 case PE__CONTAINER_AGENT_PODMAN:
216 hostname_opt = "-h ";
217 env_opt = "-e ";
218 break;
219 case PE__CONTAINER_AGENT_RKT:
220 hostname_opt = "--hostname=";
221 env_opt = "--environment=";
222 break;
223 default: // PE__CONTAINER_AGENT_UNKNOWN
225 }
226 agent_str = container_agent_str(data->agent_type);
227
228 buffer = g_string_sized_new(4096);
229
230 id = crm_strdup_printf("%s-%s-%d", data->prefix, agent_str,
231 replica->offset);
233 xml_container = create_resource(id, "heartbeat", agent_str);
234 free(id);
235
236 xml_obj = create_xml_node(xml_container, XML_TAG_ATTR_SETS);
237 crm_xml_set_id(xml_obj, "%s-attributes-%d", data->prefix, replica->offset);
238
239 crm_create_nvpair_xml(xml_obj, NULL, "image", data->image);
240 crm_create_nvpair_xml(xml_obj, NULL, "allow_pull", XML_BOOLEAN_TRUE);
241 crm_create_nvpair_xml(xml_obj, NULL, "force_kill", XML_BOOLEAN_FALSE);
242 crm_create_nvpair_xml(xml_obj, NULL, "reuse", XML_BOOLEAN_FALSE);
243
244 if (data->agent_type == PE__CONTAINER_AGENT_DOCKER) {
245 g_string_append(buffer, " --restart=no");
246 }
247
248 /* Set a container hostname only if we have an IP to map it to. The user can
249 * set -h or --uts=host themselves if they want a nicer name for logs, but
250 * this makes applications happy who need their hostname to match the IP
251 * they bind to.
252 */
253 if (data->ip_range_start != NULL) {
254 g_string_append_printf(buffer, " %s%s-%d", hostname_opt, data->prefix,
255 replica->offset);
256 }
257 pcmk__g_strcat(buffer, " ", env_opt, "PCMK_stderr=1", NULL);
258
259 if (data->container_network != NULL) {
260 pcmk__g_strcat(buffer, " --net=", data->container_network, NULL);
261 }
262
263 if (data->control_port != NULL) {
264 pcmk__g_strcat(buffer, " ", env_opt, "PCMK_remote_port=",
265 data->control_port, NULL);
266 } else {
267 g_string_append_printf(buffer, " %sPCMK_remote_port=%d", env_opt,
269 }
270
271 for (GList *iter = data->mounts; iter != NULL; iter = iter->next) {
272 pe__bundle_mount_t *mount = (pe__bundle_mount_t *) iter->data;
273 char *source = NULL;
274
275 if (pcmk_is_set(mount->flags, pe__bundle_mount_subdir)) {
276 source = crm_strdup_printf("%s/%s-%d", mount->source, data->prefix,
277 replica->offset);
278 pcmk__add_separated_word(&dbuffer, 1024, source, ",");
279 }
280
281 switch (data->agent_type) {
282 case PE__CONTAINER_AGENT_DOCKER:
283 case PE__CONTAINER_AGENT_PODMAN:
284 pcmk__g_strcat(buffer,
285 " -v ", pcmk__s(source, mount->source),
286 ":", mount->target, NULL);
287
288 if (mount->options != NULL) {
289 pcmk__g_strcat(buffer, ":", mount->options, NULL);
290 }
291 break;
292 case PE__CONTAINER_AGENT_RKT:
293 g_string_append_printf(buffer,
294 " --volume vol%d,kind=host,"
295 "source=%s%s%s "
296 "--mount volume=vol%d,target=%s",
297 volid, pcmk__s(source, mount->source),
298 (mount->options != NULL)? "," : "",
299 pcmk__s(mount->options, ""),
300 volid, mount->target);
301 volid++;
302 break;
303 default:
304 break;
305 }
306 free(source);
307 }
308
309 for (GList *iter = data->ports; iter != NULL; iter = iter->next) {
310 pe__bundle_port_t *port = (pe__bundle_port_t *) iter->data;
311
312 switch (data->agent_type) {
313 case PE__CONTAINER_AGENT_DOCKER:
314 case PE__CONTAINER_AGENT_PODMAN:
315 if (replica->ipaddr != NULL) {
316 pcmk__g_strcat(buffer,
317 " -p ", replica->ipaddr, ":", port->source,
318 ":", port->target, NULL);
319
320 } else if (!pcmk__str_eq(data->container_network, "host",
322 // No need to do port mapping if net == host
323 pcmk__g_strcat(buffer,
324 " -p ", port->source, ":", port->target,
325 NULL);
326 }
327 break;
328 case PE__CONTAINER_AGENT_RKT:
329 if (replica->ipaddr != NULL) {
330 pcmk__g_strcat(buffer,
331 " --port=", port->target,
332 ":", replica->ipaddr, ":", port->source,
333 NULL);
334 } else {
335 pcmk__g_strcat(buffer,
336 " --port=", port->target, ":", port->source,
337 NULL);
338 }
339 break;
340 default:
341 break;
342 }
343 }
344
345 /* @COMPAT: We should use pcmk__add_word() here, but we can't yet, because
346 * it would cause restarts during rolling upgrades.
347 *
348 * In a previous version of the container resource creation logic, if
349 * data->launcher_options is not NULL, we append
350 * (" %s", data->launcher_options) even if data->launcher_options is an
351 * empty string. Likewise for data->container_host_options. Using
352 *
353 * pcmk__add_word(buffer, 0, data->launcher_options)
354 *
355 * removes that extra trailing space, causing a resource definition change.
356 */
357 if (data->launcher_options != NULL) {
358 pcmk__g_strcat(buffer, " ", data->launcher_options, NULL);
359 }
360
361 if (data->container_host_options != NULL) {
362 pcmk__g_strcat(buffer, " ", data->container_host_options, NULL);
363 }
364
365 crm_create_nvpair_xml(xml_obj, NULL, "run_opts",
366 (const char *) buffer->str);
367 g_string_free(buffer, TRUE);
368
369 crm_create_nvpair_xml(xml_obj, NULL, "mount_points",
370 (dbuffer != NULL)? (const char *) dbuffer->str : "");
371 if (dbuffer != NULL) {
372 g_string_free(dbuffer, TRUE);
373 }
374
375 if (replica->child != NULL) {
376 if (data->container_command != NULL) {
377 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
378 data->container_command);
379 } else {
380 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
381 SBIN_DIR "/pacemaker-remoted");
382 }
383
384 /* TODO: Allow users to specify their own?
385 *
386 * We just want to know if the container is alive; we'll monitor the
387 * child independently.
388 */
389 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true");
390#if 0
391 /* @TODO Consider supporting the use case where we can start and stop
392 * resources, but not proxy local commands (such as setting node
393 * attributes), by running the local executor in stand-alone mode.
394 * However, this would probably be better done via ACLs as with other
395 * Pacemaker Remote nodes.
396 */
397 } else if ((child != NULL) && data->untrusted) {
398 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
399 CRM_DAEMON_DIR "/pacemaker-execd");
400 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd",
401 CRM_DAEMON_DIR "/pacemaker/cts-exec-helper -c poke");
402#endif
403 } else {
404 if (data->container_command != NULL) {
405 crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
406 data->container_command);
407 }
408
409 /* TODO: Allow users to specify their own?
410 *
411 * We don't know what's in the container, so we just want to know if it
412 * is alive.
413 */
414 crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true");
415 }
416
417 xml_obj = create_xml_node(xml_container, "operations");
418 crm_create_op_xml(xml_obj, ID(xml_container), "monitor", "60s", NULL);
419
420 // TODO: Other ops? Timeouts and intervals from underlying resource?
421 if (pe__unpack_resource(xml_container, &replica->container, parent,
422 parent->cluster) != pcmk_rc_ok) {
424 }
425 parent->children = g_list_append(parent->children, replica->container);
426
427 return pcmk_rc_ok;
428}
429
436static void
437disallow_node(pe_resource_t *rsc, const char *uname)
438{
439 gpointer match = g_hash_table_lookup(rsc->allowed_nodes, uname);
440
441 if (match) {
442 ((pe_node_t *) match)->weight = -INFINITY;
443 ((pe_node_t *) match)->rsc_discover_mode = pe_discover_never;
444 }
445 if (rsc->children) {
446 g_list_foreach(rsc->children, (GFunc) disallow_node, (gpointer) uname);
447 }
448}
449
450static int
451create_remote_resource(pe_resource_t *parent, pe__bundle_variant_data_t *data,
452 pe__bundle_replica_t *replica)
453{
454 if (replica->child && valid_network(data)) {
455 GHashTableIter gIter;
456 pe_node_t *node = NULL;
457 xmlNode *xml_remote = NULL;
458 char *id = crm_strdup_printf("%s-%d", data->prefix, replica->offset);
459 char *port_s = NULL;
460 const char *uname = NULL;
461 const char *connect_name = NULL;
462
463 if (pe_find_resource(parent->cluster->resources, id) != NULL) {
464 free(id);
465 // The biggest hammer we have
466 id = crm_strdup_printf("pcmk-internal-%s-remote-%d",
467 replica->child->id, replica->offset);
468 //@TODO return error instead of asserting?
469 CRM_ASSERT(pe_find_resource(parent->cluster->resources,
470 id) == NULL);
471 }
472
473 /* REMOTE_CONTAINER_HACK: Using "#uname" as the server name when the
474 * connection does not have its own IP is a magic string that we use to
475 * support nested remotes (i.e. a bundle running on a remote node).
476 */
477 connect_name = (replica->ipaddr? replica->ipaddr : "#uname");
478
479 if (data->control_port == NULL) {
480 port_s = pcmk__itoa(DEFAULT_REMOTE_PORT);
481 }
482
483 /* This sets replica->container as replica->remote's container, which is
484 * similar to what happens with guest nodes. This is how the scheduler
485 * knows that the bundle node is fenced by recovering the container, and
486 * that remote should be ordered relative to the container.
487 */
488 xml_remote = pe_create_remote_xml(NULL, id, replica->container->id,
489 NULL, NULL, NULL,
490 connect_name, (data->control_port?
491 data->control_port : port_s));
492 free(port_s);
493
494 /* Abandon our created ID, and pull the copy from the XML, because we
495 * need something that will get freed during data set cleanup to use as
496 * the node ID and uname.
497 */
498 free(id);
499 id = NULL;
500 uname = ID(xml_remote);
501
502 /* Ensure a node has been created for the guest (it may have already
503 * been, if it has a permanent node attribute), and ensure its weight is
504 * -INFINITY so no other resources can run on it.
505 */
506 node = pe_find_node(parent->cluster->nodes, uname);
507 if (node == NULL) {
508 node = pe_create_node(uname, uname, "remote", "-INFINITY",
509 parent->cluster);
510 } else {
511 node->weight = -INFINITY;
512 }
514
515 /* unpack_remote_nodes() ensures that each remote node and guest node
516 * has a pe_node_t entry. Ideally, it would do the same for bundle nodes.
517 * Unfortunately, a bundle has to be mostly unpacked before it's obvious
518 * what nodes will be needed, so we do it just above.
519 *
520 * Worse, that means that the node may have been utilized while
521 * unpacking other resources, without our weight correction. The most
522 * likely place for this to happen is when pe__unpack_resource() calls
523 * resource_location() to set a default score in symmetric clusters.
524 * This adds a node *copy* to each resource's allowed nodes, and these
525 * copies will have the wrong weight.
526 *
527 * As a hacky workaround, fix those copies here.
528 *
529 * @TODO Possible alternative: ensure bundles are unpacked before other
530 * resources, so the weight is correct before any copies are made.
531 */
532 g_list_foreach(parent->cluster->resources, (GFunc) disallow_node,
533 (gpointer) uname);
534
535 replica->node = pe__copy_node(node);
536 replica->node->weight = 500;
537 replica->node->rsc_discover_mode = pe_discover_exclusive;
538
539 /* Ensure the node shows up as allowed and with the correct discovery set */
540 if (replica->child->allowed_nodes != NULL) {
541 g_hash_table_destroy(replica->child->allowed_nodes);
542 }
543 replica->child->allowed_nodes = pcmk__strkey_table(NULL, free);
544 g_hash_table_insert(replica->child->allowed_nodes,
545 (gpointer) replica->node->details->id,
546 pe__copy_node(replica->node));
547
548 {
549 pe_node_t *copy = pe__copy_node(replica->node);
550 copy->weight = -INFINITY;
551 g_hash_table_insert(replica->child->parent->allowed_nodes,
552 (gpointer) replica->node->details->id, copy);
553 }
554 if (pe__unpack_resource(xml_remote, &replica->remote, parent,
555 parent->cluster) != pcmk_rc_ok) {
557 }
558
559 g_hash_table_iter_init(&gIter, replica->remote->allowed_nodes);
560 while (g_hash_table_iter_next(&gIter, NULL, (void **)&node)) {
561 if (pe__is_guest_or_remote_node(node)) {
562 /* Remote resources can only run on 'normal' cluster node */
563 node->weight = -INFINITY;
564 }
565 }
566
567 replica->node->details->remote_rsc = replica->remote;
568
569 // Ensure pe__is_guest_node() functions correctly immediately
570 replica->remote->container = replica->container;
571
572 /* A bundle's #kind is closer to "container" (guest node) than the
573 * "remote" set by pe_create_node().
574 */
575 g_hash_table_insert(replica->node->details->attrs,
576 strdup(CRM_ATTR_KIND), strdup("container"));
577
578 /* One effect of this is that setup_container() will add
579 * replica->remote to replica->container's fillers, which will make
580 * pe__resource_contains_guest_node() true for replica->container.
581 *
582 * replica->child does NOT get added to replica->container's fillers.
583 * The only noticeable effect if it did would be for its fail count to
584 * be taken into account when checking replica->container's migration
585 * threshold.
586 */
587 parent->children = g_list_append(parent->children, replica->remote);
588 }
589 return pcmk_rc_ok;
590}
591
592static int
593create_replica_resources(pe_resource_t *parent, pe__bundle_variant_data_t *data,
594 pe__bundle_replica_t *replica)
595{
596 int rc = pcmk_rc_ok;
597
598 rc = create_container_resource(parent, data, replica);
599 if (rc != pcmk_rc_ok) {
600 return rc;
601 }
602
603 rc = create_ip_resource(parent, data, replica);
604 if (rc != pcmk_rc_ok) {
605 return rc;
606 }
607
608 rc = create_remote_resource(parent, data, replica);
609 if (rc != pcmk_rc_ok) {
610 return rc;
611 }
612
613 if ((replica->child != NULL) && (replica->ipaddr != NULL)) {
614 add_hash_param(replica->child->meta, "external-ip", replica->ipaddr);
615 }
616
617 if (replica->remote != NULL) {
618 /*
619 * Allow the remote connection resource to be allocated to a
620 * different node than the one on which the container is active.
621 *
622 * This makes it possible to have Pacemaker Remote nodes running
623 * containers with pacemaker-remoted inside in order to start
624 * services inside those containers.
625 */
627 }
628 return rc;
629}
630
631static void
632mount_add(pe__bundle_variant_data_t *bundle_data, const char *source,
633 const char *target, const char *options, uint32_t flags)
634{
635 pe__bundle_mount_t *mount = calloc(1, sizeof(pe__bundle_mount_t));
636
637 CRM_ASSERT(mount != NULL);
638 mount->source = strdup(source);
639 mount->target = strdup(target);
640 pcmk__str_update(&mount->options, options);
641 mount->flags = flags;
642 bundle_data->mounts = g_list_append(bundle_data->mounts, mount);
643}
644
645static void
646mount_free(pe__bundle_mount_t *mount)
647{
648 free(mount->source);
649 free(mount->target);
650 free(mount->options);
651 free(mount);
652}
653
654static void
655port_free(pe__bundle_port_t *port)
656{
657 free(port->source);
658 free(port->target);
659 free(port);
660}
661
662static pe__bundle_replica_t *
663replica_for_remote(pe_resource_t *remote)
664{
665 pe_resource_t *top = remote;
666 pe__bundle_variant_data_t *bundle_data = NULL;
667
668 if (top == NULL) {
669 return NULL;
670 }
671
672 while (top->parent != NULL) {
673 top = top->parent;
674 }
675
676 get_bundle_variant_data(bundle_data, top);
677 for (GList *gIter = bundle_data->replicas; gIter != NULL;
678 gIter = gIter->next) {
679 pe__bundle_replica_t *replica = gIter->data;
680
681 if (replica->remote == remote) {
682 return replica;
683 }
684 }
685 CRM_LOG_ASSERT(FALSE);
686 return NULL;
687}
688
689bool
691{
692 const char *value;
693 GHashTable *params = NULL;
694
695 if (rsc == NULL) {
696 return false;
697 }
698
699 // Use NULL node since pcmk__bundle_expand() uses that to set value
700 params = pe_rsc_params(rsc, NULL, data_set);
701 value = g_hash_table_lookup(params, XML_RSC_ATTR_REMOTE_RA_ADDR);
702
703 return pcmk__str_eq(value, "#uname", pcmk__str_casei)
705}
706
707const char *
709 xmlNode *xml, const char *field)
710{
711 // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
712
713 pe_node_t *node = NULL;
714 pe__bundle_replica_t *replica = NULL;
715
717 return NULL;
718 }
719
720 replica = replica_for_remote(rsc);
721 if (replica == NULL) {
722 return NULL;
723 }
724
725 node = replica->container->allocated_to;
726 if (node == NULL) {
727 /* If it won't be running anywhere after the
728 * transition, go with where it's running now.
729 */
730 node = pe__current_node(replica->container);
731 }
732
733 if(node == NULL) {
734 crm_trace("Cannot determine address for bundle connection %s", rsc->id);
735 return NULL;
736 }
737
738 crm_trace("Setting address for bundle connection %s to bundle host %s",
739 rsc->id, pe__node_name(node));
740 if(xml != NULL && field != NULL) {
741 crm_xml_add(xml, field, node->details->uname);
742 }
743
744 return node->details->uname;
745}
746
747#define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set) do { \
748 flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \
749 "Bundle mount", ID(mount_xml), flags, \
750 (flags_to_set), #flags_to_set); \
751 } while (0)
752
753gboolean
755{
756 const char *value = NULL;
757 xmlNode *xml_obj = NULL;
758 xmlNode *xml_resource = NULL;
759 pe__bundle_variant_data_t *bundle_data = NULL;
760 bool need_log_mount = TRUE;
761
762 CRM_ASSERT(rsc != NULL);
763 pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
764
765 bundle_data = calloc(1, sizeof(pe__bundle_variant_data_t));
766 rsc->variant_opaque = bundle_data;
767 bundle_data->prefix = strdup(rsc->id);
768
769 xml_obj = first_named_child(rsc->xml, PE__CONTAINER_AGENT_DOCKER_S);
770 if (xml_obj != NULL) {
771 bundle_data->agent_type = PE__CONTAINER_AGENT_DOCKER;
772 } else {
773 xml_obj = first_named_child(rsc->xml, PE__CONTAINER_AGENT_RKT_S);
774 if (xml_obj != NULL) {
775 bundle_data->agent_type = PE__CONTAINER_AGENT_RKT;
776 } else {
777 xml_obj = first_named_child(rsc->xml, PE__CONTAINER_AGENT_PODMAN_S);
778 if (xml_obj != NULL) {
779 bundle_data->agent_type = PE__CONTAINER_AGENT_PODMAN;
780 } else {
781 return FALSE;
782 }
783 }
784 }
785
786 // Use 0 for default, minimum, and invalid promoted-max
788 if (value == NULL) {
789 // @COMPAT deprecated since 2.0.0
790 value = crm_element_value(xml_obj, "masters");
791 }
792 pcmk__scan_min_int(value, &bundle_data->promoted_max, 0);
793
794 // Default replicas to promoted-max if it was specified and 1 otherwise
795 value = crm_element_value(xml_obj, "replicas");
796 if ((value == NULL) && (bundle_data->promoted_max > 0)) {
797 bundle_data->nreplicas = bundle_data->promoted_max;
798 } else {
799 pcmk__scan_min_int(value, &bundle_data->nreplicas, 1);
800 }
801
802 /*
803 * Communication between containers on the same host via the
804 * floating IPs only works if the container is started with:
805 * --userland-proxy=false --ip-masq=false
806 */
807 value = crm_element_value(xml_obj, "replicas-per-host");
808 pcmk__scan_min_int(value, &bundle_data->nreplicas_per_host, 1);
809 if (bundle_data->nreplicas_per_host == 1) {
811 }
812
813 bundle_data->container_command = crm_element_value_copy(xml_obj, "run-command");
814 bundle_data->launcher_options = crm_element_value_copy(xml_obj, "options");
815 bundle_data->image = crm_element_value_copy(xml_obj, "image");
816 bundle_data->container_network = crm_element_value_copy(xml_obj, "network");
817
818 xml_obj = first_named_child(rsc->xml, "network");
819 if(xml_obj) {
820
821 bundle_data->ip_range_start = crm_element_value_copy(xml_obj, "ip-range-start");
822 bundle_data->host_netmask = crm_element_value_copy(xml_obj, "host-netmask");
823 bundle_data->host_network = crm_element_value_copy(xml_obj, "host-interface");
824 bundle_data->control_port = crm_element_value_copy(xml_obj, "control-port");
825 value = crm_element_value(xml_obj, "add-host");
826 if (crm_str_to_boolean(value, &bundle_data->add_host) != 1) {
827 bundle_data->add_host = TRUE;
828 }
829
830 for (xmlNode *xml_child = pcmk__xe_first_child(xml_obj); xml_child != NULL;
831 xml_child = pcmk__xe_next(xml_child)) {
832
833 pe__bundle_port_t *port = calloc(1, sizeof(pe__bundle_port_t));
834 port->source = crm_element_value_copy(xml_child, "port");
835
836 if(port->source == NULL) {
837 port->source = crm_element_value_copy(xml_child, "range");
838 } else {
839 port->target = crm_element_value_copy(xml_child, "internal-port");
840 }
841
842 if(port->source != NULL && strlen(port->source) > 0) {
843 if(port->target == NULL) {
844 port->target = strdup(port->source);
845 }
846 bundle_data->ports = g_list_append(bundle_data->ports, port);
847
848 } else {
849 pe_err("Invalid port directive %s", ID(xml_child));
850 port_free(port);
851 }
852 }
853 }
854
855 xml_obj = first_named_child(rsc->xml, "storage");
856 for (xmlNode *xml_child = pcmk__xe_first_child(xml_obj); xml_child != NULL;
857 xml_child = pcmk__xe_next(xml_child)) {
858
859 const char *source = crm_element_value(xml_child, "source-dir");
860 const char *target = crm_element_value(xml_child, "target-dir");
861 const char *options = crm_element_value(xml_child, "options");
862 int flags = pe__bundle_mount_none;
863
864 if (source == NULL) {
865 source = crm_element_value(xml_child, "source-dir-root");
867 pe__bundle_mount_subdir);
868 }
869
870 if (source && target) {
871 mount_add(bundle_data, source, target, options, flags);
872 if (strcmp(target, "/var/log") == 0) {
873 need_log_mount = FALSE;
874 }
875 } else {
876 pe_err("Invalid mount directive %s", ID(xml_child));
877 }
878 }
879
880 xml_obj = first_named_child(rsc->xml, "primitive");
881 if (xml_obj && valid_network(bundle_data)) {
882 char *value = NULL;
883 xmlNode *xml_set = NULL;
884
885 xml_resource = create_xml_node(NULL, XML_CIB_TAG_INCARNATION);
886
887 /* @COMPAT We no longer use the <master> tag, but we need to keep it as
888 * part of the resource name, so that bundles don't restart in a rolling
889 * upgrade. (It also avoids needing to change regression tests.)
890 */
891 crm_xml_set_id(xml_resource, "%s-%s", bundle_data->prefix,
892 (bundle_data->promoted_max? "master"
893 : (const char *)xml_resource->name));
894
895 xml_set = create_xml_node(xml_resource, XML_TAG_META_SETS);
896 crm_xml_set_id(xml_set, "%s-%s-meta", bundle_data->prefix, xml_resource->name);
897
898 crm_create_nvpair_xml(xml_set, NULL,
900
901 value = pcmk__itoa(bundle_data->nreplicas);
902 crm_create_nvpair_xml(xml_set, NULL,
904 free(value);
905
906 value = pcmk__itoa(bundle_data->nreplicas_per_host);
907 crm_create_nvpair_xml(xml_set, NULL,
909 free(value);
910
912 pcmk__btoa(bundle_data->nreplicas_per_host > 1));
913
914 if (bundle_data->promoted_max) {
915 crm_create_nvpair_xml(xml_set, NULL,
917
918 value = pcmk__itoa(bundle_data->promoted_max);
919 crm_create_nvpair_xml(xml_set, NULL,
921 free(value);
922 }
923
924 //crm_xml_add(xml_obj, XML_ATTR_ID, bundle_data->prefix);
925 add_node_copy(xml_resource, xml_obj);
926
927 } else if(xml_obj) {
928 pe_err("Cannot control %s inside %s without either ip-range-start or control-port",
929 rsc->id, ID(xml_obj));
930 return FALSE;
931 }
932
933 if(xml_resource) {
934 int lpc = 0;
935 GList *childIter = NULL;
936 pe__bundle_port_t *port = NULL;
937 GString *buffer = NULL;
938
939 if (pe__unpack_resource(xml_resource, &(bundle_data->child), rsc,
940 data_set) != pcmk_rc_ok) {
941 return FALSE;
942 }
943
944 /* Currently, we always map the default authentication key location
945 * into the same location inside the container.
946 *
947 * Ideally, we would respect the host's PCMK_authkey_location, but:
948 * - it may be different on different nodes;
949 * - the actual connection will do extra checking to make sure the key
950 * file exists and is readable, that we can't do here on the DC
951 * - tools such as crm_resource and crm_simulate may not have the same
952 * environment variables as the cluster, causing operation digests to
953 * differ
954 *
955 * Always using the default location inside the container is fine,
956 * because we control the pacemaker_remote environment, and it avoids
957 * having to pass another environment variable to the container.
958 *
959 * @TODO A better solution may be to have only pacemaker_remote use the
960 * environment variable, and have the cluster nodes use a new
961 * cluster option for key location. This would introduce the limitation
962 * of the location being the same on all cluster nodes, but that's
963 * reasonable.
964 */
965 mount_add(bundle_data, DEFAULT_REMOTE_KEY_LOCATION,
966 DEFAULT_REMOTE_KEY_LOCATION, NULL, pe__bundle_mount_none);
967
968 if (need_log_mount) {
969 mount_add(bundle_data, CRM_BUNDLE_DIR, "/var/log", NULL,
970 pe__bundle_mount_subdir);
971 }
972
973 port = calloc(1, sizeof(pe__bundle_port_t));
974 if(bundle_data->control_port) {
975 port->source = strdup(bundle_data->control_port);
976 } else {
977 /* If we wanted to respect PCMK_remote_port, we could use
978 * crm_default_remote_port() here and elsewhere in this file instead
979 * of DEFAULT_REMOTE_PORT.
980 *
981 * However, it gains nothing, since we control both the container
982 * environment and the connection resource parameters, and the user
983 * can use a different port if desired by setting control-port.
984 */
985 port->source = pcmk__itoa(DEFAULT_REMOTE_PORT);
986 }
987 port->target = strdup(port->source);
988 bundle_data->ports = g_list_append(bundle_data->ports, port);
989
990 buffer = g_string_sized_new(1024);
991 for (childIter = bundle_data->child->children; childIter != NULL;
992 childIter = childIter->next) {
993
994 pe__bundle_replica_t *replica = calloc(1, sizeof(pe__bundle_replica_t));
995
996 replica->child = childIter->data;
997 replica->child->exclusive_discover = TRUE;
998 replica->offset = lpc++;
999
1000 // Ensure the child's notify gets set based on the underlying primitive's value
1001 if (pcmk_is_set(replica->child->flags, pe_rsc_notify)) {
1002 pe__set_resource_flags(bundle_data->child, pe_rsc_notify);
1003 }
1004
1005 allocate_ip(bundle_data, replica, buffer);
1006 bundle_data->replicas = g_list_append(bundle_data->replicas,
1007 replica);
1008 bundle_data->attribute_target = g_hash_table_lookup(replica->child->meta,
1010 }
1011 bundle_data->container_host_options = g_string_free(buffer, FALSE);
1012
1013 if (bundle_data->attribute_target) {
1014 g_hash_table_replace(rsc->meta, strdup(XML_RSC_ATTR_TARGET),
1015 strdup(bundle_data->attribute_target));
1016 g_hash_table_replace(bundle_data->child->meta,
1017 strdup(XML_RSC_ATTR_TARGET),
1018 strdup(bundle_data->attribute_target));
1019 }
1020
1021 } else {
1022 // Just a naked container, no pacemaker-remote
1023 GString *buffer = g_string_sized_new(1024);
1024
1025 for (int lpc = 0; lpc < bundle_data->nreplicas; lpc++) {
1026 pe__bundle_replica_t *replica = calloc(1, sizeof(pe__bundle_replica_t));
1027
1028 replica->offset = lpc;
1029 allocate_ip(bundle_data, replica, buffer);
1030 bundle_data->replicas = g_list_append(bundle_data->replicas,
1031 replica);
1032 }
1033 bundle_data->container_host_options = g_string_free(buffer, FALSE);
1034 }
1035
1036 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1037 gIter = gIter->next) {
1038 pe__bundle_replica_t *replica = gIter->data;
1039
1040 if (create_replica_resources(rsc, bundle_data, replica) != pcmk_rc_ok) {
1041 pe_err("Failed unpacking resource %s", rsc->id);
1042 rsc->fns->free(rsc);
1043 return FALSE;
1044 }
1045
1046 /* Utilization needs special handling for bundles. It makes no sense for
1047 * the inner primitive to have utilization, because it is tied
1048 * one-to-one to the guest node created by the container resource -- and
1049 * there's no way to set capacities for that guest node anyway.
1050 *
1051 * What the user really wants is to configure utilization for the
1052 * container. However, the schema only allows utilization for
1053 * primitives, and the container resource is implicit anyway, so the
1054 * user can *only* configure utilization for the inner primitive. If
1055 * they do, move the primitive's utilization values to the container.
1056 *
1057 * @TODO This means that bundles without an inner primitive can't have
1058 * utilization. An alternative might be to allow utilization values in
1059 * the top-level bundle XML in the schema, and copy those to each
1060 * container.
1061 */
1062 if (replica->child != NULL) {
1063 GHashTable *empty = replica->container->utilization;
1064
1065 replica->container->utilization = replica->child->utilization;
1066 replica->child->utilization = empty;
1067 }
1068 }
1069
1070 if (bundle_data->child) {
1071 rsc->children = g_list_append(rsc->children, bundle_data->child);
1072 }
1073 return TRUE;
1074}
1075
1076static int
1077replica_resource_active(pe_resource_t *rsc, gboolean all)
1078{
1079 if (rsc) {
1080 gboolean child_active = rsc->fns->active(rsc, all);
1081
1082 if (child_active && !all) {
1083 return TRUE;
1084 } else if (!child_active && all) {
1085 return FALSE;
1086 }
1087 }
1088 return -1;
1089}
1090
1091gboolean
1093{
1094 pe__bundle_variant_data_t *bundle_data = NULL;
1095 GList *iter = NULL;
1096
1097 get_bundle_variant_data(bundle_data, rsc);
1098 for (iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
1099 pe__bundle_replica_t *replica = iter->data;
1100 int rsc_active;
1101
1102 rsc_active = replica_resource_active(replica->ip, all);
1103 if (rsc_active >= 0) {
1104 return (gboolean) rsc_active;
1105 }
1106
1107 rsc_active = replica_resource_active(replica->child, all);
1108 if (rsc_active >= 0) {
1109 return (gboolean) rsc_active;
1110 }
1111
1112 rsc_active = replica_resource_active(replica->container, all);
1113 if (rsc_active >= 0) {
1114 return (gboolean) rsc_active;
1115 }
1116
1117 rsc_active = replica_resource_active(replica->remote, all);
1118 if (rsc_active >= 0) {
1119 return (gboolean) rsc_active;
1120 }
1121 }
1122
1123 /* If "all" is TRUE, we've already checked that no resources were inactive,
1124 * so return TRUE; if "all" is FALSE, we didn't find any active resources,
1125 * so return FALSE.
1126 */
1127 return all;
1128}
1129
1141{
1142 pe__bundle_variant_data_t *bundle_data = NULL;
1143 CRM_ASSERT(bundle && node);
1144
1145 get_bundle_variant_data(bundle_data, bundle);
1146 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1147 gIter = gIter->next) {
1148 pe__bundle_replica_t *replica = gIter->data;
1149
1150 CRM_ASSERT(replica && replica->node);
1151 if (replica->node->details == node->details) {
1152 return replica->child;
1153 }
1154 }
1155 return NULL;
1156}
1157
1162static void
1163print_rsc_in_list(pe_resource_t *rsc, const char *pre_text, long options,
1164 void *print_data)
1165{
1166 if (rsc != NULL) {
1167 if (options & pe_print_html) {
1168 status_print("<li>");
1169 }
1170 rsc->fns->print(rsc, pre_text, options, print_data);
1171 if (options & pe_print_html) {
1172 status_print("</li>\n");
1173 }
1174 }
1175}
1176
1181static void
1182bundle_print_xml(pe_resource_t *rsc, const char *pre_text, long options,
1183 void *print_data)
1184{
1185 pe__bundle_variant_data_t *bundle_data = NULL;
1186 char *child_text = NULL;
1187 CRM_CHECK(rsc != NULL, return);
1188
1189 if (pre_text == NULL) {
1190 pre_text = "";
1191 }
1192 child_text = crm_strdup_printf("%s ", pre_text);
1193
1194 get_bundle_variant_data(bundle_data, rsc);
1195
1196 status_print("%s<bundle ", pre_text);
1197 status_print("id=\"%s\" ", rsc->id);
1198 status_print("type=\"%s\" ", container_agent_str(bundle_data->agent_type));
1199 status_print("image=\"%s\" ", bundle_data->image);
1200 status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_unique));
1201 status_print("managed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_managed));
1202 status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_failed));
1203 status_print(">\n");
1204
1205 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1206 gIter = gIter->next) {
1207 pe__bundle_replica_t *replica = gIter->data;
1208
1209 CRM_ASSERT(replica);
1210 status_print("%s <replica id=\"%d\">\n", pre_text, replica->offset);
1211 print_rsc_in_list(replica->ip, child_text, options, print_data);
1212 print_rsc_in_list(replica->child, child_text, options, print_data);
1213 print_rsc_in_list(replica->container, child_text, options, print_data);
1214 print_rsc_in_list(replica->remote, child_text, options, print_data);
1215 status_print("%s </replica>\n", pre_text);
1216 }
1217 status_print("%s</bundle>\n", pre_text);
1218 free(child_text);
1219}
1220
1221PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pe_resource_t *", "GList *", "GList *")
1222int
1224{
1225 uint32_t show_opts = va_arg(args, uint32_t);
1226 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
1227 GList *only_node = va_arg(args, GList *);
1228 GList *only_rsc = va_arg(args, GList *);
1229
1230 pe__bundle_variant_data_t *bundle_data = NULL;
1231 int rc = pcmk_rc_no_output;
1232 gboolean printed_header = FALSE;
1233 gboolean print_everything = TRUE;
1234
1235 CRM_ASSERT(rsc != NULL);
1236
1237 get_bundle_variant_data(bundle_data, rsc);
1238
1239 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1240 return rc;
1241 }
1242
1243 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1244
1245 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1246 gIter = gIter->next) {
1247 pe__bundle_replica_t *replica = gIter->data;
1248 char *id = NULL;
1249 gboolean print_ip, print_child, print_ctnr, print_remote;
1250
1251 CRM_ASSERT(replica);
1252
1253 if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1254 continue;
1255 }
1256
1257 print_ip = replica->ip != NULL &&
1258 !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1259 print_child = replica->child != NULL &&
1260 !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1261 print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1262 print_remote = replica->remote != NULL &&
1263 !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1264
1265 if (!print_everything && !print_ip && !print_child && !print_ctnr && !print_remote) {
1266 continue;
1267 }
1268
1269 if (!printed_header) {
1270 printed_header = TRUE;
1271
1272 rc = pe__name_and_nvpairs_xml(out, true, "bundle", 6,
1273 "id", rsc->id,
1274 "type", container_agent_str(bundle_data->agent_type),
1275 "image", bundle_data->image,
1276 "unique", pe__rsc_bool_str(rsc, pe_rsc_unique),
1277 "managed", pe__rsc_bool_str(rsc, pe_rsc_managed),
1278 "failed", pe__rsc_bool_str(rsc, pe_rsc_failed));
1279 CRM_ASSERT(rc == pcmk_rc_ok);
1280 }
1281
1282 id = pcmk__itoa(replica->offset);
1283 rc = pe__name_and_nvpairs_xml(out, true, "replica", 1, "id", id);
1284 free(id);
1285 CRM_ASSERT(rc == pcmk_rc_ok);
1286
1287 if (print_ip) {
1288 out->message(out, crm_map_element_name(replica->ip->xml), show_opts,
1289 replica->ip, only_node, only_rsc);
1290 }
1291
1292 if (print_child) {
1293 out->message(out, crm_map_element_name(replica->child->xml), show_opts,
1294 replica->child, only_node, only_rsc);
1295 }
1296
1297 if (print_ctnr) {
1298 out->message(out, crm_map_element_name(replica->container->xml), show_opts,
1299 replica->container, only_node, only_rsc);
1300 }
1301
1302 if (print_remote) {
1303 out->message(out, crm_map_element_name(replica->remote->xml), show_opts,
1304 replica->remote, only_node, only_rsc);
1305 }
1306
1307 pcmk__output_xml_pop_parent(out); // replica
1308 }
1309
1310 if (printed_header) {
1311 pcmk__output_xml_pop_parent(out); // bundle
1312 }
1313
1314 return rc;
1315}
1316
1317static void
1318pe__bundle_replica_output_html(pcmk__output_t *out, pe__bundle_replica_t *replica,
1319 pe_node_t *node, uint32_t show_opts)
1320{
1321 pe_resource_t *rsc = replica->child;
1322
1323 int offset = 0;
1324 char buffer[LINE_MAX];
1325
1326 if(rsc == NULL) {
1327 rsc = replica->container;
1328 }
1329
1330 if (replica->remote) {
1331 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1332 rsc_printable_id(replica->remote));
1333 } else {
1334 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1335 rsc_printable_id(replica->container));
1336 }
1337 if (replica->ipaddr) {
1338 offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1339 replica->ipaddr);
1340 }
1341
1342 pe__common_output_html(out, rsc, buffer, node, show_opts);
1343}
1344
1345PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pe_resource_t *", "GList *", "GList *")
1346int
1348{
1349 uint32_t show_opts = va_arg(args, uint32_t);
1350 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
1351 GList *only_node = va_arg(args, GList *);
1352 GList *only_rsc = va_arg(args, GList *);
1353
1354 pe__bundle_variant_data_t *bundle_data = NULL;
1355 int rc = pcmk_rc_no_output;
1356 gboolean print_everything = TRUE;
1357
1358 CRM_ASSERT(rsc != NULL);
1359
1360 get_bundle_variant_data(bundle_data, rsc);
1361
1362 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1363 return rc;
1364 }
1365
1366 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1367
1368 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1369 gIter = gIter->next) {
1370 pe__bundle_replica_t *replica = gIter->data;
1371 gboolean print_ip, print_child, print_ctnr, print_remote;
1372
1373 CRM_ASSERT(replica);
1374
1375 if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1376 continue;
1377 }
1378
1379 print_ip = replica->ip != NULL &&
1380 !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1381 print_child = replica->child != NULL &&
1382 !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1383 print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1384 print_remote = replica->remote != NULL &&
1385 !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1386
1387 if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
1388 (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
1389 /* The text output messages used below require pe_print_implicit to
1390 * be set to do anything.
1391 */
1392 uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
1393
1394 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s",
1395 (bundle_data->nreplicas > 1)? " set" : "",
1396 rsc->id, bundle_data->image,
1397 pcmk_is_set(rsc->flags, pe_rsc_unique) ? " (unique)" : "",
1398 pcmk_is_set(rsc->flags, pe_rsc_managed) ? "" : " (unmanaged)");
1399
1400 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1401 out->begin_list(out, NULL, NULL, "Replica[%d]", replica->offset);
1402 }
1403
1404 if (print_ip) {
1405 out->message(out, crm_map_element_name(replica->ip->xml),
1406 new_show_opts, replica->ip, only_node, only_rsc);
1407 }
1408
1409 if (print_child) {
1410 out->message(out, crm_map_element_name(replica->child->xml),
1411 new_show_opts, replica->child, only_node, only_rsc);
1412 }
1413
1414 if (print_ctnr) {
1415 out->message(out, crm_map_element_name(replica->container->xml),
1416 new_show_opts, replica->container, only_node, only_rsc);
1417 }
1418
1419 if (print_remote) {
1420 out->message(out, crm_map_element_name(replica->remote->xml),
1421 new_show_opts, replica->remote, only_node, only_rsc);
1422 }
1423
1424 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1425 out->end_list(out);
1426 }
1427 } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
1428 continue;
1429 } else {
1430 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s",
1431 (bundle_data->nreplicas > 1)? " set" : "",
1432 rsc->id, bundle_data->image,
1433 pcmk_is_set(rsc->flags, pe_rsc_unique) ? " (unique)" : "",
1434 pcmk_is_set(rsc->flags, pe_rsc_managed) ? "" : " (unmanaged)");
1435
1436 pe__bundle_replica_output_html(out, replica, pe__current_node(replica->container),
1437 show_opts);
1438 }
1439 }
1440
1441 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1442 return rc;
1443}
1444
1445static void
1446pe__bundle_replica_output_text(pcmk__output_t *out, pe__bundle_replica_t *replica,
1447 pe_node_t *node, uint32_t show_opts)
1448{
1449 pe_resource_t *rsc = replica->child;
1450
1451 int offset = 0;
1452 char buffer[LINE_MAX];
1453
1454 if(rsc == NULL) {
1455 rsc = replica->container;
1456 }
1457
1458 if (replica->remote) {
1459 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1460 rsc_printable_id(replica->remote));
1461 } else {
1462 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1463 rsc_printable_id(replica->container));
1464 }
1465 if (replica->ipaddr) {
1466 offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1467 replica->ipaddr);
1468 }
1469
1470 pe__common_output_text(out, rsc, buffer, node, show_opts);
1471}
1472
1473PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pe_resource_t *", "GList *", "GList *")
1474int
1476{
1477 uint32_t show_opts = va_arg(args, uint32_t);
1478 pe_resource_t *rsc = va_arg(args, pe_resource_t *);
1479 GList *only_node = va_arg(args, GList *);
1480 GList *only_rsc = va_arg(args, GList *);
1481
1482 pe__bundle_variant_data_t *bundle_data = NULL;
1483 int rc = pcmk_rc_no_output;
1484 gboolean print_everything = TRUE;
1485
1486 get_bundle_variant_data(bundle_data, rsc);
1487
1488 CRM_ASSERT(rsc != NULL);
1489
1490 if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1491 return rc;
1492 }
1493
1494 print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1495
1496 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1497 gIter = gIter->next) {
1498 pe__bundle_replica_t *replica = gIter->data;
1499 gboolean print_ip, print_child, print_ctnr, print_remote;
1500
1501 CRM_ASSERT(replica);
1502
1503 if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1504 continue;
1505 }
1506
1507 print_ip = replica->ip != NULL &&
1508 !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1509 print_child = replica->child != NULL &&
1510 !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1511 print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1512 print_remote = replica->remote != NULL &&
1513 !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1514
1515 if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
1516 (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
1517 /* The text output messages used below require pe_print_implicit to
1518 * be set to do anything.
1519 */
1520 uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
1521
1522 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s",
1523 (bundle_data->nreplicas > 1)? " set" : "",
1524 rsc->id, bundle_data->image,
1525 pcmk_is_set(rsc->flags, pe_rsc_unique) ? " (unique)" : "",
1526 pcmk_is_set(rsc->flags, pe_rsc_managed) ? "" : " (unmanaged)");
1527
1528 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1529 out->list_item(out, NULL, "Replica[%d]", replica->offset);
1530 }
1531
1532 out->begin_list(out, NULL, NULL, NULL);
1533
1534 if (print_ip) {
1535 out->message(out, crm_map_element_name(replica->ip->xml),
1536 new_show_opts, replica->ip, only_node, only_rsc);
1537 }
1538
1539 if (print_child) {
1540 out->message(out, crm_map_element_name(replica->child->xml),
1541 new_show_opts, replica->child, only_node, only_rsc);
1542 }
1543
1544 if (print_ctnr) {
1545 out->message(out, crm_map_element_name(replica->container->xml),
1546 new_show_opts, replica->container, only_node, only_rsc);
1547 }
1548
1549 if (print_remote) {
1550 out->message(out, crm_map_element_name(replica->remote->xml),
1551 new_show_opts, replica->remote, only_node, only_rsc);
1552 }
1553
1554 out->end_list(out);
1555 } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
1556 continue;
1557 } else {
1558 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s",
1559 (bundle_data->nreplicas > 1)? " set" : "",
1560 rsc->id, bundle_data->image,
1561 pcmk_is_set(rsc->flags, pe_rsc_unique) ? " (unique)" : "",
1562 pcmk_is_set(rsc->flags, pe_rsc_managed) ? "" : " (unmanaged)");
1563
1564 pe__bundle_replica_output_text(out, replica, pe__current_node(replica->container),
1565 show_opts);
1566 }
1567 }
1568
1569 PCMK__OUTPUT_LIST_FOOTER(out, rc);
1570 return rc;
1571}
1572
1577static void
1578print_bundle_replica(pe__bundle_replica_t *replica, const char *pre_text,
1579 long options, void *print_data)
1580{
1581 pe_node_t *node = NULL;
1582 pe_resource_t *rsc = replica->child;
1583
1584 int offset = 0;
1585 char buffer[LINE_MAX];
1586
1587 if(rsc == NULL) {
1588 rsc = replica->container;
1589 }
1590
1591 if (replica->remote) {
1592 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1593 rsc_printable_id(replica->remote));
1594 } else {
1595 offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1596 rsc_printable_id(replica->container));
1597 }
1598 if (replica->ipaddr) {
1599 offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1600 replica->ipaddr);
1601 }
1602
1603 node = pe__current_node(replica->container);
1604 common_print(rsc, pre_text, buffer, node, options, print_data);
1605}
1606
1611void
1612pe__print_bundle(pe_resource_t *rsc, const char *pre_text, long options,
1613 void *print_data)
1614{
1615 pe__bundle_variant_data_t *bundle_data = NULL;
1616 char *child_text = NULL;
1617 CRM_CHECK(rsc != NULL, return);
1618
1619 if (options & pe_print_xml) {
1620 bundle_print_xml(rsc, pre_text, options, print_data);
1621 return;
1622 }
1623
1624 get_bundle_variant_data(bundle_data, rsc);
1625
1626 if (pre_text == NULL) {
1627 pre_text = " ";
1628 }
1629
1630 status_print("%sContainer bundle%s: %s [%s]%s%s\n",
1631 pre_text, ((bundle_data->nreplicas > 1)? " set" : ""),
1632 rsc->id, bundle_data->image,
1633 pcmk_is_set(rsc->flags, pe_rsc_unique) ? " (unique)" : "",
1634 pcmk_is_set(rsc->flags, pe_rsc_managed) ? "" : " (unmanaged)");
1635 if (options & pe_print_html) {
1636 status_print("<br />\n<ul>\n");
1637 }
1638
1639
1640 for (GList *gIter = bundle_data->replicas; gIter != NULL;
1641 gIter = gIter->next) {
1642 pe__bundle_replica_t *replica = gIter->data;
1643
1644 CRM_ASSERT(replica);
1645 if (options & pe_print_html) {
1646 status_print("<li>");
1647 }
1648
1649 if (pcmk_is_set(options, pe_print_implicit)) {
1650 child_text = crm_strdup_printf(" %s", pre_text);
1651 if (pcmk__list_of_multiple(bundle_data->replicas)) {
1652 status_print(" %sReplica[%d]\n", pre_text, replica->offset);
1653 }
1654 if (options & pe_print_html) {
1655 status_print("<br />\n<ul>\n");
1656 }
1657 print_rsc_in_list(replica->ip, child_text, options, print_data);
1658 print_rsc_in_list(replica->container, child_text, options, print_data);
1659 print_rsc_in_list(replica->remote, child_text, options, print_data);
1660 print_rsc_in_list(replica->child, child_text, options, print_data);
1661 if (options & pe_print_html) {
1662 status_print("</ul>\n");
1663 }
1664 } else {
1665 child_text = crm_strdup_printf("%s ", pre_text);
1666 print_bundle_replica(replica, child_text, options, print_data);
1667 }
1668 free(child_text);
1669
1670 if (options & pe_print_html) {
1671 status_print("</li>\n");
1672 }
1673 }
1674 if (options & pe_print_html) {
1675 status_print("</ul>\n");
1676 }
1677}
1678
1679static void
1680free_bundle_replica(pe__bundle_replica_t *replica)
1681{
1682 if (replica == NULL) {
1683 return;
1684 }
1685
1686 if (replica->node) {
1687 free(replica->node);
1688 replica->node = NULL;
1689 }
1690
1691 if (replica->ip) {
1692 free_xml(replica->ip->xml);
1693 replica->ip->xml = NULL;
1694 replica->ip->fns->free(replica->ip);
1695 replica->ip = NULL;
1696 }
1697 if (replica->container) {
1698 free_xml(replica->container->xml);
1699 replica->container->xml = NULL;
1700 replica->container->fns->free(replica->container);
1701 replica->container = NULL;
1702 }
1703 if (replica->remote) {
1704 free_xml(replica->remote->xml);
1705 replica->remote->xml = NULL;
1706 replica->remote->fns->free(replica->remote);
1707 replica->remote = NULL;
1708 }
1709 free(replica->ipaddr);
1710 free(replica);
1711}
1712
1713void
1715{
1716 pe__bundle_variant_data_t *bundle_data = NULL;
1717 CRM_CHECK(rsc != NULL, return);
1718
1719 get_bundle_variant_data(bundle_data, rsc);
1720 pe_rsc_trace(rsc, "Freeing %s", rsc->id);
1721
1722 free(bundle_data->prefix);
1723 free(bundle_data->image);
1724 free(bundle_data->control_port);
1725 free(bundle_data->host_network);
1726 free(bundle_data->host_netmask);
1727 free(bundle_data->ip_range_start);
1728 free(bundle_data->container_network);
1729 free(bundle_data->launcher_options);
1730 free(bundle_data->container_command);
1731 g_free(bundle_data->container_host_options);
1732
1733 g_list_free_full(bundle_data->replicas,
1734 (GDestroyNotify) free_bundle_replica);
1735 g_list_free_full(bundle_data->mounts, (GDestroyNotify)mount_free);
1736 g_list_free_full(bundle_data->ports, (GDestroyNotify)port_free);
1737 g_list_free(rsc->children);
1738
1739 if(bundle_data->child) {
1740 free_xml(bundle_data->child->xml);
1741 bundle_data->child->xml = NULL;
1742 bundle_data->child->fns->free(bundle_data->child);
1743 }
1744 common_free(rsc);
1745}
1746
1747enum rsc_role_e
1748pe__bundle_resource_state(const pe_resource_t *rsc, gboolean current)
1749{
1750 enum rsc_role_e container_role = RSC_ROLE_UNKNOWN;
1751 return container_role;
1752}
1753
1761int
1763{
1764 if ((rsc == NULL) || (rsc->variant != pe_container)) {
1765 return 0;
1766 } else {
1767 pe__bundle_variant_data_t *bundle_data = NULL;
1768
1769 get_bundle_variant_data(bundle_data, rsc);
1770 return bundle_data->nreplicas;
1771 }
1772}
1773
1774void
1776{
1777 pe__bundle_variant_data_t *bundle_data = NULL;
1778
1779 get_bundle_variant_data(bundle_data, rsc);
1780 for (GList *item = bundle_data->replicas; item != NULL; item = item->next) {
1781 pe__bundle_replica_t *replica = item->data;
1782
1783 if (replica->ip) {
1784 replica->ip->fns->count(replica->ip);
1785 }
1786 if (replica->child) {
1787 replica->child->fns->count(replica->child);
1788 }
1789 if (replica->container) {
1790 replica->container->fns->count(replica->container);
1791 }
1792 if (replica->remote) {
1793 replica->remote->fns->count(replica->remote);
1794 }
1795 }
1796}
1797
1798gboolean
1799pe__bundle_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
1800{
1801 gboolean passes = FALSE;
1802 pe__bundle_variant_data_t *bundle_data = NULL;
1803
1805 passes = TRUE;
1806 } else {
1807 get_bundle_variant_data(bundle_data, rsc);
1808
1809 for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) {
1810 pe__bundle_replica_t *replica = gIter->data;
1811
1812 if (replica->ip != NULL && !replica->ip->fns->is_filtered(replica->ip, only_rsc, FALSE)) {
1813 passes = TRUE;
1814 break;
1815 } else if (replica->child != NULL && !replica->child->fns->is_filtered(replica->child, only_rsc, FALSE)) {
1816 passes = TRUE;
1817 break;
1818 } else if (!replica->container->fns->is_filtered(replica->container, only_rsc, FALSE)) {
1819 passes = TRUE;
1820 break;
1821 } else if (replica->remote != NULL && !replica->remote->fns->is_filtered(replica->remote, only_rsc, FALSE)) {
1822 passes = TRUE;
1823 break;
1824 }
1825 }
1826 }
1827
1828 return !passes;
1829}
#define PCMK_RESOURCE_CLASS_OCF
Definition: agents.h:27
pe_resource_t * pe__find_bundle_replica(const pe_resource_t *bundle, const pe_node_t *node)
Definition: bundle.c:1140
void pe__free_bundle(pe_resource_t *rsc)
Definition: bundle.c:1714
const char * pe__add_bundle_remote_name(pe_resource_t *rsc, pe_working_set_t *data_set, xmlNode *xml, const char *field)
Definition: bundle.c:708
int pe_bundle_replicas(const pe_resource_t *rsc)
Get the number of configured replicas in a bundle.
Definition: bundle.c:1762
#define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set)
Definition: bundle.c:747
int pe__bundle_xml(pcmk__output_t *out, va_list args)
Definition: bundle.c:1223
void pe__count_bundle(pe_resource_t *rsc)
Definition: bundle.c:1775
int pe__bundle_html(pcmk__output_t *out, va_list args)
Definition: bundle.c:1347
gboolean pe__bundle_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: bundle.c:1799
bool pe__bundle_needs_remote_name(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: bundle.c:690
int pe__bundle_text(pcmk__output_t *out, va_list args)
Definition: bundle.c:1475
gboolean pe__bundle_active(pe_resource_t *rsc, gboolean all)
Definition: bundle.c:1092
enum rsc_role_e pe__bundle_resource_state(const pe_resource_t *rsc, gboolean current)
Definition: bundle.c:1748
gboolean pe__unpack_bundle(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: bundle.c:754
void pe__print_bundle(pe_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: bundle.c:1612
const char * parent
Definition: cib.c:25
const char * name
Definition: cib.c:24
uint64_t flags
Definition: remote.c:3
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: operations.c:474
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:427
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
@ pe_print_implicit
Definition: common.h:135
@ pe_print_xml
Definition: common.h:130
@ pe_print_html
Definition: common.h:121
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
@ RSC_ROLE_UNKNOWN
Definition: common.h:93
int pe__unpack_resource(xmlNode *xml_obj, pe_resource_t **rsc, pe_resource_t *parent, pe_working_set_t *data_set)
Definition: complex.c:562
GHashTable * pe_rsc_params(pe_resource_t *rsc, const pe_node_t *node, pe_working_set_t *data_set)
Get a table of resource parameters.
Definition: complex.c:429
#define SBIN_DIR
Definition: config.h:556
#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
#define INFINITY
Definition: crm.h:99
#define CRM_ATTR_KIND
Definition: crm.h:115
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:211
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_trace(fmt, args...)
Definition: logging.h:365
#define DEFAULT_REMOTE_KEY_LOCATION
Definition: lrmd.h:49
#define DEFAULT_REMOTE_PORT
Definition: lrmd.h:51
#define ID(x)
Definition: msg_xml.h:468
#define XML_BOOLEAN_TRUE
Definition: msg_xml.h:146
#define XML_RSC_ATTR_TARGET
Definition: msg_xml.h:224
#define XML_RSC_ATTR_PROMOTED_MAX
Definition: msg_xml.h:233
#define XML_TAG_ATTR_SETS
Definition: msg_xml.h:209
#define XML_CIB_TAG_INCARNATION
Definition: msg_xml.h:219
#define XML_ATTR_ID
Definition: msg_xml.h:134
#define XML_RSC_ATTR_INCARNATION_MAX
Definition: msg_xml.h:229
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:270
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:269
#define XML_TAG_META_SETS
Definition: msg_xml.h:210
#define XML_BOOLEAN_FALSE
Definition: msg_xml.h:147
#define XML_ATTR_TYPE
Definition: msg_xml.h:138
#define XML_RSC_ATTR_ORDERED
Definition: msg_xml.h:226
#define XML_RSC_ATTR_UNIQUE
Definition: msg_xml.h:237
#define XML_RSC_ATTR_REMOTE_RA_ADDR
Definition: msg_xml.h:250
#define XML_RSC_ATTR_INCARNATION_NODEMAX
Definition: msg_xml.h:231
#define XML_CIB_TAG_RESOURCE
Definition: msg_xml.h:217
#define XML_RSC_ATTR_PROMOTABLE
Definition: msg_xml.h:232
pe_working_set_t * data_set
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
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:833
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:714
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:323
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:513
#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...)
#define pe_rsc_notify
Definition: pe_types.h:261
#define pe_rsc_managed
Definition: pe_types.h:257
#define pe_rsc_unique
Definition: pe_types.h:262
#define pe_rsc_allow_remote_remotes
Definition: pe_types.h:273
@ pe_discover_exclusive
Definition: pe_types.h:475
@ pe_discover_never
Definition: pe_types.h:474
@ pe_container
Definition: pe_types.h:41
#define pe_rsc_failed
Definition: pe_types.h:276
int pe__common_output_text(pcmk__output_t *out, pe_resource_t *rsc, const char *name, pe_node_t *node, unsigned int options)
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:544
bool pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node)
Definition: utils.c:768
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:80
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:47
void common_print(pe_resource_t *rsc, const char *pre_text, const char *name, pe_node_t *node, long options, void *print_data)
Definition: native.c:771
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:74
pe_node_t * pe__copy_node(const pe_node_t *this_node)
Definition: utils.c:89
int pe__common_output_html(pcmk__output_t *out, pe_resource_t *rsc, const char *name, pe_node_t *node, unsigned int options)
void common_free(pe_resource_t *rsc)
Definition: complex.c:926
#define pe_err(fmt...)
Definition: internal.h:49
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition: common.c:504
pe_node_t * pe_create_node(const char *id, const char *uname, const char *type, const char *score, pe_working_set_t *data_set)
Definition: unpack.c:381
bool xml_contains_remote_node(xmlNode *xml)
Definition: remote.c:84
bool pe__is_guest_or_remote_node(const pe_node_t *node)
Definition: remote.c:41
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:156
#define CRM_ASSERT(expr)
Definition: results.h:42
@ pcmk_rc_no_output
Definition: results.h:118
@ pcmk_rc_ok
Definition: results.h:148
@ pcmk_rc_unpack_error
Definition: results.h:112
Cluster status and scheduling.
pe_node_t * pe_find_node(GList *node_list, const char *uname)
Definition: status.c:443
const char * rsc_printable_id(pe_resource_t *rsc)
Definition: utils.c:568
pe_resource_t * pe_find_resource(GList *rsc_list, const char *id_rh)
Definition: status.c:391
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:883
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
void pcmk__str_update(char **str, const char *value)
Definition: strings.c:1190
@ pcmk__str_none
@ pcmk__str_star_matches
@ pcmk__str_casei
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1214
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition: strings.c:703
This structure contains everything that makes up a single output formatter.
int weight
Definition: pe_types.h:249
int rsc_discover_mode
Definition: pe_types.h:253
struct pe_node_shared_s * details
Definition: pe_types.h:252
const char * uname
Definition: pe_types.h:216
enum pe_obj_types variant
Definition: pe_types.h:338
GHashTable * meta
Definition: pe_types.h:380
GList * children
Definition: pe_types.h:384
pe_resource_t * container
Definition: pe_types.h:387
char * id
Definition: pe_types.h:329
xmlNode * xml
Definition: pe_types.h:331
GHashTable * allowed_nodes
Definition: pe_types.h:375
void * variant_opaque
Definition: pe_types.h:339
unsigned long long flags
Definition: pe_types.h:355
pe_resource_t * parent
Definition: pe_types.h:336
resource_object_functions_t * fns
Definition: pe_types.h:340
gboolean(* is_filtered)(pe_resource_t *, GList *, gboolean)
Definition: pe_types.h:58
gboolean(* active)(pe_resource_t *, gboolean)
Definition: pe_types.h:53
void(* free)(pe_resource_t *)
Definition: pe_types.h:56
void(* print)(pe_resource_t *, const char *, long, void *)
Definition: pe_types.h:52
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2930
void crm_xml_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
void free_xml(xmlNode *child)
Definition: xml.c:885
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:1190
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition: xml.c:727
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:749