pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
bundle.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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>
17 #include <crm/pengine/internal.h>
18 #include <crm/common/xml.h>
19 #include <crm/common/output.h>
21 #include <pe_status_private.h>
22 
25 
26  // mount instance-specific subdirectory rather than source directly
28 };
29 
30 typedef 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 
37 typedef struct {
38  char *source;
39  char *target;
40 } pe__bundle_port_t;
41 
47 };
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 
54 typedef 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; // pcmk__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  pcmk__assert(pcmk__is_bundle(rsc) && (rsc->variant_opaque != NULL)); \
83  data = (pe__bundle_variant_data_t *) rsc->variant_opaque;
84 
93 int
95 {
96  const pe__bundle_variant_data_t *bundle_data = NULL;
97 
98  get_bundle_variant_data(bundle_data, pe__const_top_resource(rsc, true));
99  return bundle_data->nreplicas;
100 }
101 
112 {
113  const pe__bundle_variant_data_t *bundle_data = NULL;
114 
115  get_bundle_variant_data(bundle_data, pe__const_top_resource(rsc, true));
116  return bundle_data->child;
117 }
118 
128 const pcmk_resource_t *
130 {
131  const pe__bundle_variant_data_t *data = NULL;
132  const pcmk_resource_t *top = pe__const_top_resource(instance, true);
133 
134  if (!pcmk__is_bundle(top)) {
135  return NULL;
136  }
138 
139  for (const GList *iter = data->replicas; iter != NULL; iter = iter->next) {
140  const pcmk__bundle_replica_t *replica = iter->data;
141 
142  if (instance == replica->container) {
143  return replica->child;
144  }
145  }
146  return NULL;
147 }
148 
158 bool
160  const pcmk_node_t *node)
161 {
162  pe__bundle_variant_data_t *bundle_data = NULL;
163 
164  get_bundle_variant_data(bundle_data, bundle);
165  for (GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
166  pcmk__bundle_replica_t *replica = iter->data;
167 
168  if (pcmk__same_node(node, replica->node)) {
169  return true;
170  }
171  }
172  return false;
173 }
174 
186 {
187  const pe__bundle_variant_data_t *bundle_data = NULL;
188  const pcmk__bundle_replica_t *replica = NULL;
189 
190  get_bundle_variant_data(bundle_data, bundle);
191  if (bundle_data->replicas == NULL) {
192  return NULL;
193  }
194  replica = bundle_data->replicas->data;
195  return replica->container;
196 }
197 
207 void
209  bool (*fn)(pcmk__bundle_replica_t *, void *),
210  void *user_data)
211 {
212  const pe__bundle_variant_data_t *bundle_data = NULL;
213 
214  get_bundle_variant_data(bundle_data, bundle);
215  for (GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
216  if (!fn((pcmk__bundle_replica_t *) iter->data, user_data)) {
217  break;
218  }
219  }
220 }
221 
231 void
233  bool (*fn)(const pcmk__bundle_replica_t *,
234  void *),
235  void *user_data)
236 {
237  const pe__bundle_variant_data_t *bundle_data = NULL;
238 
239  get_bundle_variant_data(bundle_data, bundle);
240  for (const GList *iter = bundle_data->replicas; iter != NULL;
241  iter = iter->next) {
242 
243  if (!fn((const pcmk__bundle_replica_t *) iter->data, user_data)) {
244  break;
245  }
246  }
247 }
248 
249 static char *
250 next_ip(const char *last_ip)
251 {
252  unsigned int oct1 = 0;
253  unsigned int oct2 = 0;
254  unsigned int oct3 = 0;
255  unsigned int oct4 = 0;
256  int rc = sscanf(last_ip, "%u.%u.%u.%u", &oct1, &oct2, &oct3, &oct4);
257 
258  if (rc != 4) {
259  /*@ TODO check for IPv6 */
260  return NULL;
261 
262  } else if (oct3 > 253) {
263  return NULL;
264 
265  } else if (oct4 > 253) {
266  ++oct3;
267  oct4 = 1;
268 
269  } else {
270  ++oct4;
271  }
272 
273  return crm_strdup_printf("%u.%u.%u.%u", oct1, oct2, oct3, oct4);
274 }
275 
276 static void
278  GString *buffer)
279 {
280  if(data->ip_range_start == NULL) {
281  return;
282 
283  } else if(data->ip_last) {
284  replica->ipaddr = next_ip(data->ip_last);
285 
286  } else {
287  replica->ipaddr = strdup(data->ip_range_start);
288  }
289 
290  data->ip_last = replica->ipaddr;
291  switch (data->agent_type) {
294  if (data->add_host) {
295  g_string_append_printf(buffer, " --add-host=%s-%d:%s",
296  data->prefix, replica->offset,
297  replica->ipaddr);
298  } else {
299  g_string_append_printf(buffer, " --hosts-entry=%s=%s-%d",
300  replica->ipaddr, data->prefix,
301  replica->offset);
302  }
303  break;
304 
306  g_string_append_printf(buffer, " --hosts-entry=%s=%s-%d",
307  replica->ipaddr, data->prefix,
308  replica->offset);
309  break;
310 
311  default: // PE__CONTAINER_AGENT_UNKNOWN
312  break;
313  }
314 }
315 
316 static xmlNode *
317 create_resource(const char *name, const char *provider, const char *kind)
318 {
319  xmlNode *rsc = pcmk__xe_create(NULL, PCMK_XE_PRIMITIVE);
320 
321  crm_xml_add(rsc, PCMK_XA_ID, name);
323  crm_xml_add(rsc, PCMK_XA_PROVIDER, provider);
324  crm_xml_add(rsc, PCMK_XA_TYPE, kind);
325 
326  return rsc;
327 }
328 
341 static bool
342 valid_network(pe__bundle_variant_data_t *data)
343 {
344  if(data->ip_range_start) {
345  return TRUE;
346  }
347  if(data->control_port) {
348  if(data->nreplicas_per_host > 1) {
349  pcmk__config_err("Specifying the '" PCMK_XA_CONTROL_PORT "' for %s "
350  "requires '" PCMK_XA_REPLICAS_PER_HOST "=1'",
351  data->prefix);
352  data->nreplicas_per_host = 1;
353  // @TODO to be sure:
354  // pcmk__clear_rsc_flags(rsc, pcmk_rsc_unique);
355  }
356  return TRUE;
357  }
358  return FALSE;
359 }
360 
361 static int
363  pcmk__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 = pcmk__xe_create(xml_ip, PCMK_XE_INSTANCE_ATTRIBUTES);
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 = pcmk__xe_create(xml_ip, PCMK_XE_OPERATIONS);
393  crm_create_op_xml(xml_obj, pcmk__xe_id(xml_ip), PCMK_ACTION_MONITOR,
394  "60s", NULL);
395 
396  // TODO: Other ops? Timeouts and intervals from underlying resource?
397 
398  if (pe__unpack_resource(xml_ip, &replica->ip, parent,
399  parent->cluster) != pcmk_rc_ok) {
400  return pcmk_rc_unpack_error;
401  }
402 
403  parent->children = g_list_append(parent->children, replica->ip);
404  }
405  return pcmk_rc_ok;
406 }
407 
408 static const char*
409 container_agent_str(enum pe__container_agent t)
410 {
411  switch (t) {
415  default: // PE__CONTAINER_AGENT_UNKNOWN
416  break;
417  }
419 }
420 
421 static int
422 create_container_resource(pcmk_resource_t *parent,
424  pcmk__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
451  return pcmk_rc_unpack_error;
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 = pcmk__xe_create(xml_container, PCMK_XE_INSTANCE_ATTRIBUTES);
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", PCMK_VALUE_TRUE);
468  crm_create_nvpair_xml(xml_obj, NULL, "force_kill", PCMK_VALUE_FALSE);
469  crm_create_nvpair_xml(xml_obj, NULL, "reuse", PCMK_VALUE_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,
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 = pcmk__xe_create(xml_container, PCMK_XE_OPERATIONS);
645  crm_create_op_xml(xml_obj, pcmk__xe_id(xml_container), PCMK_ACTION_MONITOR,
646  "60s", NULL);
647 
648  // TODO: Other ops? Timeouts and intervals from underlying resource?
649  if (pe__unpack_resource(xml_container, &replica->container, parent,
650  parent->cluster) != pcmk_rc_ok) {
651  return pcmk_rc_unpack_error;
652  }
654  parent->children = g_list_append(parent->children, replica->container);
655 
656  return pcmk_rc_ok;
657 }
658 
665 static void
666 disallow_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 = -PCMK_SCORE_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 
679 static int
680 create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data,
681  pcmk__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  pcmk__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 = pcmk__xe_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 = pcmk_find_node(parent->cluster, uname);
736  if (node == NULL) {
738  -PCMK_SCORE_INFINITY, parent->cluster);
739  } else {
740  node->weight = -PCMK_SCORE_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 = -PCMK_SCORE_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,
784  parent->cluster) != pcmk_rc_ok) {
785  return pcmk_rc_unpack_error;
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 (pcmk__is_pacemaker_remote_node(node)) {
791  /* Remote resources can only run on 'normal' cluster node */
792  node->weight = -PCMK_SCORE_INFINITY;
793  }
794  }
795 
796  replica->node->details->remote_rsc = replica->remote;
797 
798  // Ensure pcmk__is_guest_or_bundle_node() functions correctly
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  pcmk__insert_dup(replica->node->details->attrs,
805  CRM_ATTR_KIND, "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 
821 static int
822 create_replica_resources(pcmk_resource_t *parent,
824  pcmk__bundle_replica_t *replica)
825 {
826  int rc = pcmk_rc_ok;
827 
828  rc = create_container_resource(parent, data, replica);
829  if (rc != pcmk_rc_ok) {
830  return rc;
831  }
832 
833  rc = create_ip_resource(parent, data, replica);
834  if (rc != pcmk_rc_ok) {
835  return rc;
836  }
837 
838  rc = create_remote_resource(parent, data, replica);
839  if (rc != pcmk_rc_ok) {
840  return rc;
841  }
842 
843  if ((replica->child != NULL) && (replica->ipaddr != NULL)) {
844  pcmk__insert_meta(replica->child, "external-ip", replica->ipaddr);
845  }
846 
847  if (replica->remote != NULL) {
848  /*
849  * Allow the remote connection resource to be allocated to a
850  * different node than the one on which the container is active.
851  *
852  * This makes it possible to have Pacemaker Remote nodes running
853  * containers with pacemaker-remoted inside in order to start
854  * services inside those containers.
855  */
857  }
858  return rc;
859 }
860 
861 static void
862 mount_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 = pcmk__assert_alloc(1,
866  sizeof(pe__bundle_mount_t));
867 
868  mount->source = pcmk__str_copy(source);
869  mount->target = pcmk__str_copy(target);
870  mount->options = pcmk__str_copy(options);
871  mount->flags = flags;
872  bundle_data->mounts = g_list_append(bundle_data->mounts, mount);
873 }
874 
875 static void
876 mount_free(pe__bundle_mount_t *mount)
877 {
878  free(mount->source);
879  free(mount->target);
880  free(mount->options);
881  free(mount);
882 }
883 
884 static void
885 port_free(pe__bundle_port_t *port)
886 {
887  free(port->source);
888  free(port->target);
889  free(port);
890 }
891 
892 static pcmk__bundle_replica_t *
893 replica_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  pcmk__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 
919 bool
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, PCMK_REMOTE_RA_ADDR);
932 
933  return pcmk__str_eq(value, "#uname", pcmk__str_casei)
934  && xml_contains_remote_node(rsc->xml);
935 }
936 
937 const char *
939  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  pcmk__bundle_replica_t *replica = NULL;
945 
946  if (!pe__bundle_needs_remote_name(rsc)) {
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 = pcmk__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, pcmk__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", pcmk__xe_id(mount_xml), \
980  flags, (flags_to_set), #flags_to_set); \
981  } while (0)
982 
983 gboolean
985 {
986  const char *value = NULL;
987  xmlNode *xml_obj = NULL;
988  const xmlNode *xml_child = NULL;
989  xmlNode *xml_resource = NULL;
990  pe__bundle_variant_data_t *bundle_data = NULL;
991  bool need_log_mount = TRUE;
992 
993  pcmk__assert(rsc != NULL);
994  pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
995 
996  bundle_data = pcmk__assert_alloc(1, sizeof(pe__bundle_variant_data_t));
997  rsc->variant_opaque = bundle_data;
998  bundle_data->prefix = strdup(rsc->id);
999 
1000  xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_DOCKER, NULL, NULL);
1001  if (xml_obj != NULL) {
1002  bundle_data->agent_type = PE__CONTAINER_AGENT_DOCKER;
1003  } else {
1004  xml_obj = pcmk__xe_first_child(rsc->xml, PCMK__XE_RKT, NULL, NULL);
1005  if (xml_obj != NULL) {
1007  "Support for " PCMK__XE_RKT " in bundles "
1008  "(such as %s) is deprecated and will be "
1009  "removed in a future release", rsc->id);
1010  bundle_data->agent_type = PE__CONTAINER_AGENT_RKT;
1011  } else {
1012  xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_PODMAN, NULL,
1013  NULL);
1014  if (xml_obj != NULL) {
1015  bundle_data->agent_type = PE__CONTAINER_AGENT_PODMAN;
1016  } else {
1017  return FALSE;
1018  }
1019  }
1020  }
1021 
1022  // Use 0 for default, minimum, and invalid PCMK_XA_PROMOTED_MAX
1023  value = crm_element_value(xml_obj, PCMK_XA_PROMOTED_MAX);
1024  if (value == NULL) {
1025  // @COMPAT deprecated since 2.0.0
1027 
1028  if (value != NULL) {
1030  "Support for the " PCMK__XA_PROMOTED_MAX_LEGACY
1031  " attribute (such as in %s) is deprecated and "
1032  "will be removed in a future release. Use "
1033  PCMK_XA_PROMOTED_MAX " instead.",
1034  rsc->id);
1035  }
1036  }
1037  pcmk__scan_min_int(value, &bundle_data->promoted_max, 0);
1038 
1039  /* Default replicas to PCMK_XA_PROMOTED_MAX if it was specified and 1
1040  * otherwise
1041  */
1042  value = crm_element_value(xml_obj, PCMK_XA_REPLICAS);
1043  if ((value == NULL) && (bundle_data->promoted_max > 0)) {
1044  bundle_data->nreplicas = bundle_data->promoted_max;
1045  } else {
1046  pcmk__scan_min_int(value, &bundle_data->nreplicas, 1);
1047  }
1048 
1049  /*
1050  * Communication between containers on the same host via the
1051  * floating IPs only works if the container is started with:
1052  * --userland-proxy=false --ip-masq=false
1053  */
1054  value = crm_element_value(xml_obj, PCMK_XA_REPLICAS_PER_HOST);
1055  pcmk__scan_min_int(value, &bundle_data->nreplicas_per_host, 1);
1056  if (bundle_data->nreplicas_per_host == 1) {
1058  }
1059 
1060  bundle_data->container_command =
1062  bundle_data->launcher_options = crm_element_value_copy(xml_obj,
1063  PCMK_XA_OPTIONS);
1064  bundle_data->image = crm_element_value_copy(xml_obj, PCMK_XA_IMAGE);
1065  bundle_data->container_network = crm_element_value_copy(xml_obj,
1066  PCMK_XA_NETWORK);
1067 
1068  xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_NETWORK, NULL, NULL);
1069  if(xml_obj) {
1070  bundle_data->ip_range_start =
1072  bundle_data->host_netmask =
1074  bundle_data->host_network =
1076  bundle_data->control_port =
1078  value = crm_element_value(xml_obj, PCMK_XA_ADD_HOST);
1079  if (crm_str_to_boolean(value, &bundle_data->add_host) != 1) {
1080  bundle_data->add_host = TRUE;
1081  }
1082 
1083  for (xml_child = pcmk__xe_first_child(xml_obj, PCMK_XE_PORT_MAPPING,
1084  NULL, NULL);
1085  xml_child != NULL; xml_child = pcmk__xe_next_same(xml_child)) {
1086 
1087  pe__bundle_port_t *port =
1088  pcmk__assert_alloc(1, sizeof(pe__bundle_port_t));
1089 
1090  port->source = crm_element_value_copy(xml_child, PCMK_XA_PORT);
1091 
1092  if(port->source == NULL) {
1093  port->source = crm_element_value_copy(xml_child, PCMK_XA_RANGE);
1094  } else {
1095  port->target = crm_element_value_copy(xml_child,
1097  }
1098 
1099  if(port->source != NULL && strlen(port->source) > 0) {
1100  if(port->target == NULL) {
1101  port->target = strdup(port->source);
1102  }
1103  bundle_data->ports = g_list_append(bundle_data->ports, port);
1104 
1105  } else {
1106  pcmk__config_err("Invalid " PCMK_XA_PORT " directive %s",
1107  pcmk__xe_id(xml_child));
1108  port_free(port);
1109  }
1110  }
1111  }
1112 
1113  xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_STORAGE, NULL, NULL);
1114  for (xml_child = pcmk__xe_first_child(xml_obj, PCMK_XE_STORAGE_MAPPING,
1115  NULL, NULL);
1116  xml_child != NULL; xml_child = pcmk__xe_next_same(xml_child)) {
1117 
1118  const char *source = crm_element_value(xml_child, PCMK_XA_SOURCE_DIR);
1119  const char *target = crm_element_value(xml_child, PCMK_XA_TARGET_DIR);
1120  const char *options = crm_element_value(xml_child, PCMK_XA_OPTIONS);
1122 
1123  if (source == NULL) {
1124  source = crm_element_value(xml_child, PCMK_XA_SOURCE_DIR_ROOT);
1125  pe__set_bundle_mount_flags(xml_child, flags,
1127  }
1128 
1129  if (source && target) {
1130  mount_add(bundle_data, source, target, options, flags);
1131  if (strcmp(target, "/var/log") == 0) {
1132  need_log_mount = FALSE;
1133  }
1134  } else {
1135  pcmk__config_err("Invalid mount directive %s",
1136  pcmk__xe_id(xml_child));
1137  }
1138  }
1139 
1140  xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_PRIMITIVE, NULL, NULL);
1141  if (xml_obj && valid_network(bundle_data)) {
1142  char *value = NULL;
1143  xmlNode *xml_set = NULL;
1144 
1145  xml_resource = pcmk__xe_create(NULL, PCMK_XE_CLONE);
1146 
1147  /* @COMPAT We no longer use the <master> tag, but we need to keep it as
1148  * part of the resource name, so that bundles don't restart in a rolling
1149  * upgrade. (It also avoids needing to change regression tests.)
1150  */
1151  crm_xml_set_id(xml_resource, "%s-%s", bundle_data->prefix,
1152  (bundle_data->promoted_max? "master"
1153  : (const char *)xml_resource->name));
1154 
1155  xml_set = pcmk__xe_create(xml_resource, PCMK_XE_META_ATTRIBUTES);
1156  crm_xml_set_id(xml_set, "%s-%s-meta", bundle_data->prefix, xml_resource->name);
1157 
1158  crm_create_nvpair_xml(xml_set, NULL,
1160 
1161  value = pcmk__itoa(bundle_data->nreplicas);
1162  crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_MAX, value);
1163  free(value);
1164 
1165  value = pcmk__itoa(bundle_data->nreplicas_per_host);
1166  crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_NODE_MAX, value);
1167  free(value);
1168 
1170  pcmk__btoa(bundle_data->nreplicas_per_host > 1));
1171 
1172  if (bundle_data->promoted_max) {
1173  crm_create_nvpair_xml(xml_set, NULL,
1175 
1176  value = pcmk__itoa(bundle_data->promoted_max);
1177  crm_create_nvpair_xml(xml_set, NULL, PCMK_META_PROMOTED_MAX, value);
1178  free(value);
1179  }
1180 
1181  //crm_xml_add(xml_obj, PCMK_XA_ID, bundle_data->prefix);
1182  pcmk__xml_copy(xml_resource, xml_obj);
1183 
1184  } else if(xml_obj) {
1185  pcmk__config_err("Cannot control %s inside %s without either "
1187  rsc->id, pcmk__xe_id(xml_obj));
1188  return FALSE;
1189  }
1190 
1191  if(xml_resource) {
1192  int lpc = 0;
1193  GList *childIter = NULL;
1194  pe__bundle_port_t *port = NULL;
1195  GString *buffer = NULL;
1196 
1197  if (pe__unpack_resource(xml_resource, &(bundle_data->child), rsc,
1198  scheduler) != pcmk_rc_ok) {
1199  return FALSE;
1200  }
1201 
1202  /* Currently, we always map the default authentication key location
1203  * into the same location inside the container.
1204  *
1205  * Ideally, we would respect the host's PCMK_authkey_location, but:
1206  * - it may be different on different nodes;
1207  * - the actual connection will do extra checking to make sure the key
1208  * file exists and is readable, that we can't do here on the DC
1209  * - tools such as crm_resource and crm_simulate may not have the same
1210  * environment variables as the cluster, causing operation digests to
1211  * differ
1212  *
1213  * Always using the default location inside the container is fine,
1214  * because we control the pacemaker_remote environment, and it avoids
1215  * having to pass another environment variable to the container.
1216  *
1217  * @TODO A better solution may be to have only pacemaker_remote use the
1218  * environment variable, and have the cluster nodes use a new
1219  * cluster option for key location. This would introduce the limitation
1220  * of the location being the same on all cluster nodes, but that's
1221  * reasonable.
1222  */
1223  mount_add(bundle_data, DEFAULT_REMOTE_KEY_LOCATION,
1225 
1226  if (need_log_mount) {
1227  mount_add(bundle_data, CRM_BUNDLE_DIR, "/var/log", NULL,
1229  }
1230 
1231  port = pcmk__assert_alloc(1, sizeof(pe__bundle_port_t));
1232  if(bundle_data->control_port) {
1233  port->source = strdup(bundle_data->control_port);
1234  } else {
1235  /* If we wanted to respect PCMK_remote_port, we could use
1236  * crm_default_remote_port() here and elsewhere in this file instead
1237  * of DEFAULT_REMOTE_PORT.
1238  *
1239  * However, it gains nothing, since we control both the container
1240  * environment and the connection resource parameters, and the user
1241  * can use a different port if desired by setting
1242  * PCMK_XA_CONTROL_PORT.
1243  */
1244  port->source = pcmk__itoa(DEFAULT_REMOTE_PORT);
1245  }
1246  port->target = strdup(port->source);
1247  bundle_data->ports = g_list_append(bundle_data->ports, port);
1248 
1249  buffer = g_string_sized_new(1024);
1250  for (childIter = bundle_data->child->children; childIter != NULL;
1251  childIter = childIter->next) {
1252 
1253  pcmk__bundle_replica_t *replica = NULL;
1254 
1255  replica = pcmk__assert_alloc(1, sizeof(pcmk__bundle_replica_t));
1256  replica->child = childIter->data;
1257  replica->child->exclusive_discover = TRUE;
1258  replica->offset = lpc++;
1259 
1260  // Ensure the child's notify gets set based on the underlying primitive's value
1261  if (pcmk_is_set(replica->child->flags, pcmk_rsc_notify)) {
1262  pcmk__set_rsc_flags(bundle_data->child, pcmk_rsc_notify);
1263  }
1264 
1265  allocate_ip(bundle_data, replica, buffer);
1266  bundle_data->replicas = g_list_append(bundle_data->replicas,
1267  replica);
1268  bundle_data->attribute_target =
1269  g_hash_table_lookup(replica->child->meta,
1271  }
1272  bundle_data->container_host_options = g_string_free(buffer, FALSE);
1273 
1274  if (bundle_data->attribute_target) {
1276  bundle_data->attribute_target);
1277  pcmk__insert_dup(bundle_data->child->meta,
1279  bundle_data->attribute_target);
1280  }
1281 
1282  } else {
1283  // Just a naked container, no pacemaker-remote
1284  GString *buffer = g_string_sized_new(1024);
1285 
1286  for (int lpc = 0; lpc < bundle_data->nreplicas; lpc++) {
1287  pcmk__bundle_replica_t *replica = NULL;
1288 
1289  replica = pcmk__assert_alloc(1, sizeof(pcmk__bundle_replica_t));
1290  replica->offset = lpc;
1291  allocate_ip(bundle_data, replica, buffer);
1292  bundle_data->replicas = g_list_append(bundle_data->replicas,
1293  replica);
1294  }
1295  bundle_data->container_host_options = g_string_free(buffer, FALSE);
1296  }
1297 
1298  for (GList *gIter = bundle_data->replicas; gIter != NULL;
1299  gIter = gIter->next) {
1300  pcmk__bundle_replica_t *replica = gIter->data;
1301 
1302  if (create_replica_resources(rsc, bundle_data, replica) != pcmk_rc_ok) {
1303  pcmk__config_err("Failed unpacking resource %s", rsc->id);
1304  rsc->fns->free(rsc);
1305  return FALSE;
1306  }
1307 
1308  /* Utilization needs special handling for bundles. It makes no sense for
1309  * the inner primitive to have utilization, because it is tied
1310  * one-to-one to the guest node created by the container resource -- and
1311  * there's no way to set capacities for that guest node anyway.
1312  *
1313  * What the user really wants is to configure utilization for the
1314  * container. However, the schema only allows utilization for
1315  * primitives, and the container resource is implicit anyway, so the
1316  * user can *only* configure utilization for the inner primitive. If
1317  * they do, move the primitive's utilization values to the container.
1318  *
1319  * @TODO This means that bundles without an inner primitive can't have
1320  * utilization. An alternative might be to allow utilization values in
1321  * the top-level bundle XML in the schema, and copy those to each
1322  * container.
1323  */
1324  if (replica->child != NULL) {
1325  GHashTable *empty = replica->container->utilization;
1326 
1327  replica->container->utilization = replica->child->utilization;
1328  replica->child->utilization = empty;
1329  }
1330  }
1331 
1332  if (bundle_data->child) {
1333  rsc->children = g_list_append(rsc->children, bundle_data->child);
1334  }
1335  return TRUE;
1336 }
1337 
1338 static int
1339 replica_resource_active(pcmk_resource_t *rsc, gboolean all)
1340 {
1341  if (rsc) {
1342  gboolean child_active = rsc->fns->active(rsc, all);
1343 
1344  if (child_active && !all) {
1345  return TRUE;
1346  } else if (!child_active && all) {
1347  return FALSE;
1348  }
1349  }
1350  return -1;
1351 }
1352 
1353 gboolean
1355 {
1356  pe__bundle_variant_data_t *bundle_data = NULL;
1357  GList *iter = NULL;
1358 
1359  get_bundle_variant_data(bundle_data, rsc);
1360  for (iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
1361  pcmk__bundle_replica_t *replica = iter->data;
1362  int rsc_active;
1363 
1364  rsc_active = replica_resource_active(replica->ip, all);
1365  if (rsc_active >= 0) {
1366  return (gboolean) rsc_active;
1367  }
1368 
1369  rsc_active = replica_resource_active(replica->child, all);
1370  if (rsc_active >= 0) {
1371  return (gboolean) rsc_active;
1372  }
1373 
1374  rsc_active = replica_resource_active(replica->container, all);
1375  if (rsc_active >= 0) {
1376  return (gboolean) rsc_active;
1377  }
1378 
1379  rsc_active = replica_resource_active(replica->remote, all);
1380  if (rsc_active >= 0) {
1381  return (gboolean) rsc_active;
1382  }
1383  }
1384 
1385  /* If "all" is TRUE, we've already checked that no resources were inactive,
1386  * so return TRUE; if "all" is FALSE, we didn't find any active resources,
1387  * so return FALSE.
1388  */
1389  return all;
1390 }
1391 
1403 {
1404  pe__bundle_variant_data_t *bundle_data = NULL;
1405 
1406  pcmk__assert((bundle != NULL) && (node != NULL));
1407 
1408  get_bundle_variant_data(bundle_data, bundle);
1409  for (GList *gIter = bundle_data->replicas; gIter != NULL;
1410  gIter = gIter->next) {
1411  pcmk__bundle_replica_t *replica = gIter->data;
1412 
1413  pcmk__assert((replica != NULL) && (replica->node != NULL));
1414  if (pcmk__same_node(replica->node, node)) {
1415  return replica->child;
1416  }
1417  }
1418  return NULL;
1419 }
1420 
1425 static void
1426 print_rsc_in_list(pcmk_resource_t *rsc, const char *pre_text, long options,
1427  void *print_data)
1428 {
1429  if (rsc != NULL) {
1430  if (options & pe_print_html) {
1431  status_print("<li>");
1432  }
1433  rsc->fns->print(rsc, pre_text, options, print_data);
1434  if (options & pe_print_html) {
1435  status_print("</li>\n");
1436  }
1437  }
1438 }
1439 
1444 static void
1445 bundle_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
1446  void *print_data)
1447 {
1448  pe__bundle_variant_data_t *bundle_data = NULL;
1449  char *child_text = NULL;
1450  CRM_CHECK(rsc != NULL, return);
1451 
1452  if (pre_text == NULL) {
1453  pre_text = "";
1454  }
1455  child_text = crm_strdup_printf("%s ", pre_text);
1456 
1457  get_bundle_variant_data(bundle_data, rsc);
1458 
1459  status_print("%s<bundle ", pre_text);
1460  status_print(PCMK_XA_ID "=\"%s\" ", rsc->id);
1461  status_print("type=\"%s\" ", container_agent_str(bundle_data->agent_type));
1462  status_print("image=\"%s\" ", bundle_data->image);
1463  status_print("unique=\"%s\" ",
1464  pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
1465  status_print("managed=\"%s\" ",
1466  pcmk__flag_text(rsc->flags, pcmk_rsc_managed));
1467  status_print("failed=\"%s\" ",
1468  pcmk__flag_text(rsc->flags, pcmk_rsc_failed));
1469  status_print(">\n");
1470 
1471  for (GList *gIter = bundle_data->replicas; gIter != NULL;
1472  gIter = gIter->next) {
1473  pcmk__bundle_replica_t *replica = gIter->data;
1474 
1475  pcmk__assert(replica);
1476  status_print("%s <replica " PCMK_XA_ID "=\"%d\">\n",
1477  pre_text, replica->offset);
1478  print_rsc_in_list(replica->ip, child_text, options, print_data);
1479  print_rsc_in_list(replica->child, child_text, options, print_data);
1480  print_rsc_in_list(replica->container, child_text, options, print_data);
1481  print_rsc_in_list(replica->remote, child_text, options, print_data);
1482  status_print("%s </replica>\n", pre_text);
1483  }
1484  status_print("%s</bundle>\n", pre_text);
1485  free(child_text);
1486 }
1487 
1488 PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1489  "GList *")
1490 int
1491 pe__bundle_xml(pcmk__output_t *out, va_list args)
1492 {
1493  uint32_t show_opts = va_arg(args, uint32_t);
1494  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1495  GList *only_node = va_arg(args, GList *);
1496  GList *only_rsc = va_arg(args, GList *);
1497 
1498  pe__bundle_variant_data_t *bundle_data = NULL;
1499  int rc = pcmk_rc_no_output;
1500  gboolean printed_header = FALSE;
1501  gboolean print_everything = TRUE;
1502 
1503  const char *desc = NULL;
1504 
1505  pcmk__assert(rsc != NULL);
1506 
1507  get_bundle_variant_data(bundle_data, rsc);
1508 
1509  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1510  return rc;
1511  }
1512 
1513  print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1514 
1515  for (GList *gIter = bundle_data->replicas; gIter != NULL;
1516  gIter = gIter->next) {
1517  pcmk__bundle_replica_t *replica = gIter->data;
1518  char *id = NULL;
1519  gboolean print_ip, print_child, print_ctnr, print_remote;
1520 
1521  pcmk__assert(replica);
1522 
1523  if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1524  continue;
1525  }
1526 
1527  print_ip = replica->ip != NULL &&
1528  !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1529  print_child = replica->child != NULL &&
1530  !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1531  print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1532  print_remote = replica->remote != NULL &&
1533  !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1534 
1535  if (!print_everything && !print_ip && !print_child && !print_ctnr && !print_remote) {
1536  continue;
1537  }
1538 
1539  if (!printed_header) {
1540  const char *type = container_agent_str(bundle_data->agent_type);
1541  const char *unique = pcmk__flag_text(rsc->flags, pcmk_rsc_unique);
1542  const char *maintenance = pcmk__flag_text(rsc->flags,
1544  const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed);
1545  const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed);
1546 
1547  printed_header = TRUE;
1548 
1549  desc = pe__resource_description(rsc, show_opts);
1550 
1551  rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_BUNDLE,
1552  PCMK_XA_ID, rsc->id,
1553  PCMK_XA_TYPE, type,
1554  PCMK_XA_IMAGE, bundle_data->image,
1555  PCMK_XA_UNIQUE, unique,
1556  PCMK_XA_MAINTENANCE, maintenance,
1557  PCMK_XA_MANAGED, managed,
1558  PCMK_XA_FAILED, failed,
1559  PCMK_XA_DESCRIPTION, desc,
1560  NULL);
1561  pcmk__assert(rc == pcmk_rc_ok);
1562  }
1563 
1564  id = pcmk__itoa(replica->offset);
1565  rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_REPLICA,
1566  PCMK_XA_ID, id,
1567  NULL);
1568  free(id);
1569  pcmk__assert(rc == pcmk_rc_ok);
1570 
1571  if (print_ip) {
1572  out->message(out, (const char *) replica->ip->xml->name, show_opts,
1573  replica->ip, only_node, only_rsc);
1574  }
1575 
1576  if (print_child) {
1577  out->message(out, (const char *) replica->child->xml->name,
1578  show_opts, replica->child, only_node, only_rsc);
1579  }
1580 
1581  if (print_ctnr) {
1582  out->message(out, (const char *) replica->container->xml->name,
1583  show_opts, replica->container, only_node, only_rsc);
1584  }
1585 
1586  if (print_remote) {
1587  out->message(out, (const char *) replica->remote->xml->name,
1588  show_opts, replica->remote, only_node, only_rsc);
1589  }
1590 
1591  pcmk__output_xml_pop_parent(out); // replica
1592  }
1593 
1594  if (printed_header) {
1595  pcmk__output_xml_pop_parent(out); // bundle
1596  }
1597 
1598  return rc;
1599 }
1600 
1601 static void
1602 pe__bundle_replica_output_html(pcmk__output_t *out,
1603  pcmk__bundle_replica_t *replica,
1604  pcmk_node_t *node, uint32_t show_opts)
1605 {
1606  pcmk_resource_t *rsc = replica->child;
1607 
1608  int offset = 0;
1609  char buffer[LINE_MAX];
1610 
1611  if(rsc == NULL) {
1612  rsc = replica->container;
1613  }
1614 
1615  if (replica->remote) {
1616  offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1617  rsc_printable_id(replica->remote));
1618  } else {
1619  offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1620  rsc_printable_id(replica->container));
1621  }
1622  if (replica->ipaddr) {
1623  offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1624  replica->ipaddr);
1625  }
1626 
1627  pe__common_output_html(out, rsc, buffer, node, show_opts);
1628 }
1629 
1639 static const char *
1640 get_unmanaged_str(const pcmk_resource_t *rsc)
1641 {
1642  if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
1643  return " (maintenance)";
1644  }
1645  if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
1646  return " (unmanaged)";
1647  }
1648  return "";
1649 }
1650 
1651 PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1652  "GList *")
1653 int
1654 pe__bundle_html(pcmk__output_t *out, va_list args)
1655 {
1656  uint32_t show_opts = va_arg(args, uint32_t);
1657  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1658  GList *only_node = va_arg(args, GList *);
1659  GList *only_rsc = va_arg(args, GList *);
1660 
1661  const char *desc = NULL;
1662  pe__bundle_variant_data_t *bundle_data = NULL;
1663  int rc = pcmk_rc_no_output;
1664  gboolean print_everything = TRUE;
1665 
1666  pcmk__assert(rsc != NULL);
1667 
1668  get_bundle_variant_data(bundle_data, rsc);
1669 
1670  desc = pe__resource_description(rsc, show_opts);
1671 
1672  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1673  return rc;
1674  }
1675 
1676  print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1677 
1678  for (GList *gIter = bundle_data->replicas; gIter != NULL;
1679  gIter = gIter->next) {
1680  pcmk__bundle_replica_t *replica = gIter->data;
1681  gboolean print_ip, print_child, print_ctnr, print_remote;
1682 
1683  pcmk__assert(replica);
1684 
1685  if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1686  continue;
1687  }
1688 
1689  print_ip = replica->ip != NULL &&
1690  !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1691  print_child = replica->child != NULL &&
1692  !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1693  print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1694  print_remote = replica->remote != NULL &&
1695  !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1696 
1697  if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
1698  (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
1699  /* The text output messages used below require pe_print_implicit to
1700  * be set to do anything.
1701  */
1702  uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
1703 
1704  PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1705  (bundle_data->nreplicas > 1)? " set" : "",
1706  rsc->id, bundle_data->image,
1707  pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1708  desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1709  get_unmanaged_str(rsc));
1710 
1711  if (pcmk__list_of_multiple(bundle_data->replicas)) {
1712  out->begin_list(out, NULL, NULL, "Replica[%d]", replica->offset);
1713  }
1714 
1715  if (print_ip) {
1716  out->message(out, (const char *) replica->ip->xml->name,
1717  new_show_opts, replica->ip, only_node, only_rsc);
1718  }
1719 
1720  if (print_child) {
1721  out->message(out, (const char *) replica->child->xml->name,
1722  new_show_opts, replica->child, only_node,
1723  only_rsc);
1724  }
1725 
1726  if (print_ctnr) {
1727  out->message(out, (const char *) replica->container->xml->name,
1728  new_show_opts, replica->container, only_node,
1729  only_rsc);
1730  }
1731 
1732  if (print_remote) {
1733  out->message(out, (const char *) replica->remote->xml->name,
1734  new_show_opts, replica->remote, only_node,
1735  only_rsc);
1736  }
1737 
1738  if (pcmk__list_of_multiple(bundle_data->replicas)) {
1739  out->end_list(out);
1740  }
1741  } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
1742  continue;
1743  } else {
1744  PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1745  (bundle_data->nreplicas > 1)? " set" : "",
1746  rsc->id, bundle_data->image,
1747  pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1748  desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1749  get_unmanaged_str(rsc));
1750 
1751  pe__bundle_replica_output_html(out, replica,
1752  pcmk__current_node(replica->container),
1753  show_opts);
1754  }
1755  }
1756 
1757  PCMK__OUTPUT_LIST_FOOTER(out, rc);
1758  return rc;
1759 }
1760 
1761 static void
1762 pe__bundle_replica_output_text(pcmk__output_t *out,
1763  pcmk__bundle_replica_t *replica,
1764  pcmk_node_t *node, uint32_t show_opts)
1765 {
1766  const pcmk_resource_t *rsc = replica->child;
1767 
1768  int offset = 0;
1769  char buffer[LINE_MAX];
1770 
1771  if(rsc == NULL) {
1772  rsc = replica->container;
1773  }
1774 
1775  if (replica->remote) {
1776  offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1777  rsc_printable_id(replica->remote));
1778  } else {
1779  offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1780  rsc_printable_id(replica->container));
1781  }
1782  if (replica->ipaddr) {
1783  offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1784  replica->ipaddr);
1785  }
1786 
1787  pe__common_output_text(out, rsc, buffer, node, show_opts);
1788 }
1789 
1790 PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
1791  "GList *")
1792 int
1793 pe__bundle_text(pcmk__output_t *out, va_list args)
1794 {
1795  uint32_t show_opts = va_arg(args, uint32_t);
1796  pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1797  GList *only_node = va_arg(args, GList *);
1798  GList *only_rsc = va_arg(args, GList *);
1799 
1800  const char *desc = NULL;
1801  pe__bundle_variant_data_t *bundle_data = NULL;
1802  int rc = pcmk_rc_no_output;
1803  gboolean print_everything = TRUE;
1804 
1805  desc = pe__resource_description(rsc, show_opts);
1806 
1807  get_bundle_variant_data(bundle_data, rsc);
1808 
1809  pcmk__assert(rsc != NULL);
1810 
1811  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1812  return rc;
1813  }
1814 
1815  print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
1816 
1817  for (GList *gIter = bundle_data->replicas; gIter != NULL;
1818  gIter = gIter->next) {
1819  pcmk__bundle_replica_t *replica = gIter->data;
1820  gboolean print_ip, print_child, print_ctnr, print_remote;
1821 
1822  pcmk__assert(replica);
1823 
1824  if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
1825  continue;
1826  }
1827 
1828  print_ip = replica->ip != NULL &&
1829  !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
1830  print_child = replica->child != NULL &&
1831  !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
1832  print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
1833  print_remote = replica->remote != NULL &&
1834  !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
1835 
1836  if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
1837  (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
1838  /* The text output messages used below require pe_print_implicit to
1839  * be set to do anything.
1840  */
1841  uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
1842 
1843  PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1844  (bundle_data->nreplicas > 1)? " set" : "",
1845  rsc->id, bundle_data->image,
1846  pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1847  desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1848  get_unmanaged_str(rsc));
1849 
1850  if (pcmk__list_of_multiple(bundle_data->replicas)) {
1851  out->list_item(out, NULL, "Replica[%d]", replica->offset);
1852  }
1853 
1854  out->begin_list(out, NULL, NULL, NULL);
1855 
1856  if (print_ip) {
1857  out->message(out, (const char *) replica->ip->xml->name,
1858  new_show_opts, replica->ip, only_node, only_rsc);
1859  }
1860 
1861  if (print_child) {
1862  out->message(out, (const char *) replica->child->xml->name,
1863  new_show_opts, replica->child, only_node,
1864  only_rsc);
1865  }
1866 
1867  if (print_ctnr) {
1868  out->message(out, (const char *) replica->container->xml->name,
1869  new_show_opts, replica->container, only_node,
1870  only_rsc);
1871  }
1872 
1873  if (print_remote) {
1874  out->message(out, (const char *) replica->remote->xml->name,
1875  new_show_opts, replica->remote, only_node,
1876  only_rsc);
1877  }
1878 
1879  out->end_list(out);
1880  } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
1881  continue;
1882  } else {
1883  PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
1884  (bundle_data->nreplicas > 1)? " set" : "",
1885  rsc->id, bundle_data->image,
1886  pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1887  desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
1888  get_unmanaged_str(rsc));
1889 
1890  pe__bundle_replica_output_text(out, replica,
1891  pcmk__current_node(replica->container),
1892  show_opts);
1893  }
1894  }
1895 
1896  PCMK__OUTPUT_LIST_FOOTER(out, rc);
1897  return rc;
1898 }
1899 
1904 static void
1905 print_bundle_replica(pcmk__bundle_replica_t *replica, const char *pre_text,
1906  long options, void *print_data)
1907 {
1908  pcmk_node_t *node = NULL;
1909  pcmk_resource_t *rsc = replica->child;
1910 
1911  int offset = 0;
1912  char buffer[LINE_MAX];
1913 
1914  if(rsc == NULL) {
1915  rsc = replica->container;
1916  }
1917 
1918  if (replica->remote) {
1919  offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1920  rsc_printable_id(replica->remote));
1921  } else {
1922  offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
1923  rsc_printable_id(replica->container));
1924  }
1925  if (replica->ipaddr) {
1926  offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
1927  replica->ipaddr);
1928  }
1929 
1930  node = pcmk__current_node(replica->container);
1931  common_print(rsc, pre_text, buffer, node, options, print_data);
1932 }
1933 
1938 void
1939 pe__print_bundle(pcmk_resource_t *rsc, const char *pre_text, long options,
1940  void *print_data)
1941 {
1942  pe__bundle_variant_data_t *bundle_data = NULL;
1943  char *child_text = NULL;
1944  CRM_CHECK(rsc != NULL, return);
1945 
1946  if (options & pe_print_xml) {
1947  bundle_print_xml(rsc, pre_text, options, print_data);
1948  return;
1949  }
1950 
1951  get_bundle_variant_data(bundle_data, rsc);
1952 
1953  if (pre_text == NULL) {
1954  pre_text = " ";
1955  }
1956 
1957  status_print("%sContainer bundle%s: %s [%s]%s%s\n",
1958  pre_text, ((bundle_data->nreplicas > 1)? " set" : ""),
1959  rsc->id, bundle_data->image,
1960  pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
1961  pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " (unmanaged)");
1962  if (options & pe_print_html) {
1963  status_print("<br />\n<ul>\n");
1964  }
1965 
1966 
1967  for (GList *gIter = bundle_data->replicas; gIter != NULL;
1968  gIter = gIter->next) {
1969  pcmk__bundle_replica_t *replica = gIter->data;
1970 
1971  pcmk__assert(replica);
1972  if (options & pe_print_html) {
1973  status_print("<li>");
1974  }
1975 
1976  if (pcmk_is_set(options, pe_print_implicit)) {
1977  child_text = crm_strdup_printf(" %s", pre_text);
1978  if (pcmk__list_of_multiple(bundle_data->replicas)) {
1979  status_print(" %sReplica[%d]\n", pre_text, replica->offset);
1980  }
1981  if (options & pe_print_html) {
1982  status_print("<br />\n<ul>\n");
1983  }
1984  print_rsc_in_list(replica->ip, child_text, options, print_data);
1985  print_rsc_in_list(replica->container, child_text, options, print_data);
1986  print_rsc_in_list(replica->remote, child_text, options, print_data);
1987  print_rsc_in_list(replica->child, child_text, options, print_data);
1988  if (options & pe_print_html) {
1989  status_print("</ul>\n");
1990  }
1991  } else {
1992  child_text = crm_strdup_printf("%s ", pre_text);
1993  print_bundle_replica(replica, child_text, options, print_data);
1994  }
1995  free(child_text);
1996 
1997  if (options & pe_print_html) {
1998  status_print("</li>\n");
1999  }
2000  }
2001  if (options & pe_print_html) {
2002  status_print("</ul>\n");
2003  }
2004 }
2005 
2006 static void
2007 free_bundle_replica(pcmk__bundle_replica_t *replica)
2008 {
2009  if (replica == NULL) {
2010  return;
2011  }
2012 
2013  if (replica->node) {
2014  free(replica->node);
2015  replica->node = NULL;
2016  }
2017 
2018  if (replica->ip) {
2019  free_xml(replica->ip->xml);
2020  replica->ip->xml = NULL;
2021  replica->ip->fns->free(replica->ip);
2022  replica->ip = NULL;
2023  }
2024  if (replica->container) {
2025  free_xml(replica->container->xml);
2026  replica->container->xml = NULL;
2027  replica->container->fns->free(replica->container);
2028  replica->container = NULL;
2029  }
2030  if (replica->remote) {
2031  free_xml(replica->remote->xml);
2032  replica->remote->xml = NULL;
2033  replica->remote->fns->free(replica->remote);
2034  replica->remote = NULL;
2035  }
2036  free(replica->ipaddr);
2037  free(replica);
2038 }
2039 
2040 void
2042 {
2043  pe__bundle_variant_data_t *bundle_data = NULL;
2044  CRM_CHECK(rsc != NULL, return);
2045 
2046  get_bundle_variant_data(bundle_data, rsc);
2047  pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
2048 
2049  free(bundle_data->prefix);
2050  free(bundle_data->image);
2051  free(bundle_data->control_port);
2052  free(bundle_data->host_network);
2053  free(bundle_data->host_netmask);
2054  free(bundle_data->ip_range_start);
2055  free(bundle_data->container_network);
2056  free(bundle_data->launcher_options);
2057  free(bundle_data->container_command);
2058  g_free(bundle_data->container_host_options);
2059 
2060  g_list_free_full(bundle_data->replicas,
2061  (GDestroyNotify) free_bundle_replica);
2062  g_list_free_full(bundle_data->mounts, (GDestroyNotify)mount_free);
2063  g_list_free_full(bundle_data->ports, (GDestroyNotify)port_free);
2064  g_list_free(rsc->children);
2065 
2066  if(bundle_data->child) {
2067  free_xml(bundle_data->child->xml);
2068  bundle_data->child->xml = NULL;
2069  bundle_data->child->fns->free(bundle_data->child);
2070  }
2071  common_free(rsc);
2072 }
2073 
2074 enum rsc_role_e
2075 pe__bundle_resource_state(const pcmk_resource_t *rsc, gboolean current)
2076 {
2077  enum rsc_role_e container_role = pcmk_role_unknown;
2078  return container_role;
2079 }
2080 
2088 int
2090 {
2091  if (pcmk__is_bundle(rsc)) {
2092  pe__bundle_variant_data_t *bundle_data = NULL;
2093 
2094  get_bundle_variant_data(bundle_data, rsc);
2095  return bundle_data->nreplicas;
2096  }
2097  return 0;
2098 }
2099 
2100 void
2102 {
2103  pe__bundle_variant_data_t *bundle_data = NULL;
2104 
2105  get_bundle_variant_data(bundle_data, rsc);
2106  for (GList *item = bundle_data->replicas; item != NULL; item = item->next) {
2107  pcmk__bundle_replica_t *replica = item->data;
2108 
2109  if (replica->ip) {
2110  replica->ip->fns->count(replica->ip);
2111  }
2112  if (replica->child) {
2113  replica->child->fns->count(replica->child);
2114  }
2115  if (replica->container) {
2116  replica->container->fns->count(replica->container);
2117  }
2118  if (replica->remote) {
2119  replica->remote->fns->count(replica->remote);
2120  }
2121  }
2122 }
2123 
2124 gboolean
2125 pe__bundle_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
2126  gboolean check_parent)
2127 {
2128  gboolean passes = FALSE;
2129  pe__bundle_variant_data_t *bundle_data = NULL;
2130 
2132  passes = TRUE;
2133  } else {
2134  get_bundle_variant_data(bundle_data, rsc);
2135 
2136  for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) {
2137  pcmk__bundle_replica_t *replica = gIter->data;
2138 
2139  if (replica->ip != NULL && !replica->ip->fns->is_filtered(replica->ip, only_rsc, FALSE)) {
2140  passes = TRUE;
2141  break;
2142  } else if (replica->child != NULL && !replica->child->fns->is_filtered(replica->child, only_rsc, FALSE)) {
2143  passes = TRUE;
2144  break;
2145  } else if (!replica->container->fns->is_filtered(replica->container, only_rsc, FALSE)) {
2146  passes = TRUE;
2147  break;
2148  } else if (replica->remote != NULL && !replica->remote->fns->is_filtered(replica->remote, only_rsc, FALSE)) {
2149  passes = TRUE;
2150  break;
2151  }
2152  }
2153  }
2154 
2155  return !passes;
2156 }
2157 
2168 GList *
2170 {
2171  GList *containers = NULL;
2172  const pe__bundle_variant_data_t *data = NULL;
2173 
2174  get_bundle_variant_data(data, bundle);
2175  for (GList *iter = data->replicas; iter != NULL; iter = iter->next) {
2176  pcmk__bundle_replica_t *replica = iter->data;
2177 
2178  containers = g_list_append(containers, replica->container);
2179  }
2180  return containers;
2181 }
2182 
2183 // Bundle implementation of pcmk_rsc_methods_t:active_node()
2184 pcmk_node_t *
2185 pe__bundle_active_node(const pcmk_resource_t *rsc, unsigned int *count_all,
2186  unsigned int *count_clean)
2187 {
2188  pcmk_node_t *active = NULL;
2189  pcmk_node_t *node = NULL;
2190  pcmk_resource_t *container = NULL;
2191  GList *containers = NULL;
2192  GList *iter = NULL;
2193  GHashTable *nodes = NULL;
2194  const pe__bundle_variant_data_t *data = NULL;
2195 
2196  if (count_all != NULL) {
2197  *count_all = 0;
2198  }
2199  if (count_clean != NULL) {
2200  *count_clean = 0;
2201  }
2202  if (rsc == NULL) {
2203  return NULL;
2204  }
2205 
2206  /* For the purposes of this method, we only care about where the bundle's
2207  * containers are active, so build a list of active containers.
2208  */
2210  for (iter = data->replicas; iter != NULL; iter = iter->next) {
2211  pcmk__bundle_replica_t *replica = iter->data;
2212 
2213  if (replica->container->running_on != NULL) {
2214  containers = g_list_append(containers, replica->container);
2215  }
2216  }
2217  if (containers == NULL) {
2218  return NULL;
2219  }
2220 
2221  /* If the bundle has only a single active container, just use that
2222  * container's method. If live migration is ever supported for bundle
2223  * containers, this will allow us to prefer the migration source when there
2224  * is only one container and it is migrating. For now, this just lets us
2225  * avoid creating the nodes table.
2226  */
2227  if (pcmk__list_of_1(containers)) {
2228  container = containers->data;
2229  node = container->fns->active_node(container, count_all, count_clean);
2230  g_list_free(containers);
2231  return node;
2232  }
2233 
2234  // Add all containers' active nodes to a hash table (for uniqueness)
2235  nodes = g_hash_table_new(NULL, NULL);
2236  for (iter = containers; iter != NULL; iter = iter->next) {
2237  container = iter->data;
2238 
2239  for (GList *node_iter = container->running_on; node_iter != NULL;
2240  node_iter = node_iter->next) {
2241  node = node_iter->data;
2242 
2243  // If insert returns true, we haven't counted this node yet
2244  if (g_hash_table_insert(nodes, (gpointer) node->details,
2245  (gpointer) node)
2246  && !pe__count_active_node(rsc, node, &active, count_all,
2247  count_clean)) {
2248  goto done;
2249  }
2250  }
2251  }
2252 
2253 done:
2254  g_list_free(containers);
2255  g_hash_table_destroy(nodes);
2256  return active;
2257 }
2258 
2267 unsigned int
2269 {
2270  pe__bundle_variant_data_t *bundle_data = NULL;
2271 
2272  get_bundle_variant_data(bundle_data, rsc);
2273  pcmk__assert(bundle_data->nreplicas_per_host >= 0);
2274  return (unsigned int) bundle_data->nreplicas_per_host;
2275 }
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition: complex.c:1071
void pe__print_bundle(pcmk_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: bundle.c:1939
#define PCMK__XA_PROMOTED_MAX_LEGACY
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
pcmk_node_t * pcmk_find_node(const pcmk_scheduler_t *scheduler, const char *node_name)
Find a node by name in scheduler data.
Definition: scheduler.c:103
void pe__count_bundle(pcmk_resource_t *rsc)
Definition: bundle.c:2101
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:974
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition: utils.c:89
pcmk_scheduler_t * cluster
Definition: resources.h:408
GHashTable * attrs
Definition: nodes.h:143
#define PCMK_XA_REPLICAS_PER_HOST
Definition: xml_names.h:379
#define PCMK_META_PROMOTABLE
Definition: options.h:101
char data[0]
Definition: cpg.c:58
#define PCMK_XA_MANAGED
Definition: xml_names.h:323
#define CRM_ATTR_KIND
Definition: crm.h:101
Control output from tools.
pcmk_node_t * node
Node created for this instance.
#define PCMK_XE_STORAGE
Definition: xml_names.h:205
#define PCMK_VALUE_FALSE
Definition: options.h:152
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:126
gboolean pe__bundle_active(pcmk_resource_t *rsc, gboolean all)
Definition: bundle.c:1354
const char * name
Definition: cib.c:26
#define PCMK_XE_PRIMITIVE
Definition: xml_names.h:164
#define PCMK_XE_NETWORK
Definition: xml_names.h:135
#define PE__CONTAINER_AGENT_DOCKER_S
Definition: bundle.c:50
GList * children
Definition: resources.h:471
#define pcmk__rsc_trace(rsc, fmt, args...)
#define PCMK_META_CONTAINER_ATTRIBUTE_TARGET
Definition: options.h:85
pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id)
Definition: status.c:435
gboolean pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
Definition: bundle.c:984
xmlNode * xml
Definition: resources.h:400
gboolean exclusive_discover
Definition: resources.h:432
#define pcmk__insert_meta(obj, name, value)
#define pcmk__config_err(fmt...)
pcmk_resource_t * ip
IP address resource for ipaddr.
#define PCMK_ACTION_MONITOR
Definition: actions.h:60
const char * pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts)
Definition: pe_output.c:22
GHashTable * meta
Definition: resources.h:467
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:819
#define PCMK_REMOTE_RA_ADDR
Definition: options.h:122
#define PCMK_XE_STORAGE_MAPPING
Definition: xml_names.h:206
#define PCMK_XA_INTERNAL_PORT
Definition: xml_names.h:308
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:313
pcmk_node_t * pe_create_node(const char *id, const char *uname, const char *type, int score, pcmk_scheduler_t *scheduler)
Definition: unpack.c:470
#define PCMK__ENV_REMOTE_PORT
#define PCMK_XE_PORT_MAPPING
Definition: xml_names.h:162
#define PCMK_XA_ADD_HOST
Definition: xml_names.h:231
#define PCMK_XA_PROVIDER
Definition: xml_names.h:364
const pcmk_resource_t * pe__get_rsc_in_container(const pcmk_resource_t *instance)
Definition: bundle.c:129
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition: strings.c:806
#define PCMK_RESOURCE_CLASS_OCF
Definition: agents.h:27
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:228
#define PCMK_META_CLONE_MAX
Definition: options.h:82
enum crm_ais_msg_types type
Definition: cpg.c:51
#define PCMK_XA_RANGE
Definition: xml_names.h:368
const char * rsc_printable_id(const pcmk_resource_t *rsc)
Definition: utils.c:553
char * ipaddr
IP address associated with this instance.
#define PCMK_XE_BUNDLE
Definition: xml_names.h:72
unsigned int pe__bundle_max_per_node(const pcmk_resource_t *rsc)
Definition: bundle.c:2268
void(* print)(pcmk_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: resources.h:295
#define PCMK__XE_RKT
pcmk_resource_t * container
Definition: resources.h:476
#define PCMK_XA_IMAGE
Definition: xml_names.h:304
#define PCMK_XA_TYPE
Definition: xml_names.h:430
#define CRM_BUNDLE_DIR
Definition: config.h:14
int pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc, pcmk_resource_t *parent, pcmk_scheduler_t *scheduler)
Definition: complex.c:691
int pe__bundle_html(pcmk__output_t *out, va_list args)
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:1065
#define PCMK_XA_NETWORK
Definition: xml_names.h:331
#define DEFAULT_REMOTE_PORT
Definition: lrmd.h:67
#define PCMK_XA_FAILED
Definition: xml_names.h:283
#define DEFAULT_REMOTE_KEY_LOCATION
Definition: lrmd.h:65
#define get_bundle_variant_data(data, rsc)
Definition: bundle.c:81
struct pe__bundle_variant_data_s pe__bundle_variant_data_t
int pe__bundle_max(const pcmk_resource_t *rsc)
Definition: bundle.c:94
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:125
#define PCMK_VALUE_HOST
Definition: options.h:160
#define PCMK_XE_CLONE
Definition: xml_names.h:80
#define PCMK_META_GLOBALLY_UNIQUE
Definition: options.h:89
#define PCMK_XE_REPLICA
Definition: xml_names.h:171
int weight
Definition: nodes.h:163
pcmk_resource_t * parent
Definition: resources.h:409
void pe__foreach_const_bundle_replica(const pcmk_resource_t *bundle, bool(*fn)(const pcmk__bundle_replica_t *, void *), void *user_data)
Definition: bundle.c:232
void common_free(pcmk_resource_t *rsc)
Definition: complex.c:1088
void(* free)(pcmk_resource_t *rsc)
Definition: resources.h:336
int pe__bundle_text(pcmk__output_t *out, va_list args)
#define PCMK_XE_PODMAN
Definition: xml_names.h:161
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:758
#define PCMK_META_CLONE_NODE_MAX
Definition: options.h:84
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:458
int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name,...) G_GNUC_NULL_TERMINATED
Definition: pe_output.c:611
bool pe__bundle_needs_remote_name(pcmk_resource_t *rsc)
Definition: bundle.c:920
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
#define CRM_DAEMON_DIR
Definition: config.h:24
#define PCMK_XA_DESCRIPTION
Definition: xml_names.h:261
#define crm_trace(fmt, args...)
Definition: logging.h:404
PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *", "GList *")
Definition: bundle.c:1488
#define PE__CONTAINER_AGENT_PODMAN_S
Definition: bundle.c:52
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1308
bool xml_contains_remote_node(xmlNode *xml)
Definition: remote.c:47
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:94
void crm_xml_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
struct pe_node_shared_s * details
Definition: nodes.h:168
#define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set)
Definition: bundle.c:977
pcmk_resource_t * pe__first_container(const pcmk_resource_t *bundle)
Definition: bundle.c:185
unsigned long long flags
Definition: resources.h:428
const char * uname
Definition: nodes.h:74
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:34
#define PCMK_XA_PORT
Definition: xml_names.h:357
GHashTable * utilization
Definition: resources.h:469
#define PCMK_VALUE_TRUE
Definition: options.h:215
#define PCMK_XA_ID
Definition: xml_names.h:301
#define PCMK_XE_OPERATIONS
Definition: xml_names.h:151
#define pcmk__set_rsc_flags(resource, flags_to_set)
enum rsc_role_e pe__bundle_resource_state(const pcmk_resource_t *rsc, gboolean current)
Definition: bundle.c:2075
void free_xml(xmlNode *child)
Definition: xml.c:958
#define pcmk__str_copy(str)
#define pcmk__warn_once(wo_flag, fmt...)
pcmk_resource_t * child
Instance of bundled resource.
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:562
GList * pe__bundle_containers(const pcmk_resource_t *bundle)
Definition: bundle.c:2169
int rsc_discover_mode
Definition: nodes.h:171
bool pe__node_is_bundle_instance(const pcmk_resource_t *bundle, const pcmk_node_t *node)
Definition: bundle.c:159
const char * id
Definition: nodes.h:73
#define PCMK_XA_CONTROL_PORT
Definition: xml_names.h:251
#define PCMK_XA_MAINTENANCE
Definition: xml_names.h:321
#define PCMK_XE_META_ATTRIBUTES
Definition: xml_names.h:130
A single instance of a bundle.
#define pcmk__assert(expr)
#define PE__CONTAINER_AGENT_UNKNOWN_S
Definition: bundle.c:49
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
#define PCMK_XA_IP_RANGE_START
Definition: xml_names.h:310
const char * target
Definition: pcmk_fence.c:29
#define PCMK_XA_TARGET_DIR
Definition: xml_names.h:420
pcmk_resource_t * container
Container associated with this instance.
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:508
gboolean(* active)(pcmk_resource_t *rsc, gboolean all)
Definition: resources.h:306
int pe__bundle_xml(pcmk__output_t *out, va_list args)
#define SBIN_DIR
Definition: config.h:589
#define PCMK_XA_SOURCE_DIR
Definition: xml_names.h:402
#define PCMK_XA_CLASS
Definition: xml_names.h:246
#define PE__CONTAINER_AGENT_RKT_S
Definition: bundle.c:51
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
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)
Cluster status and scheduling.
const char * pe__add_bundle_remote_name(pcmk_resource_t *rsc, xmlNode *xml, const char *field)
Definition: bundle.c:938
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:695
pcmk_rsc_methods_t * fns
Definition: resources.h:412
#define PCMK_XA_REPLICAS
Definition: xml_names.h:378
void * variant_opaque
Definition: resources.h:411
pcmk_scheduler_t * scheduler
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)
#define PCMK_XA_PROMOTED_MAX
Definition: xml_names.h:362
pcmk_resource_t * pe__find_bundle_replica(const pcmk_resource_t *bundle, const pcmk_node_t *node)
Definition: bundle.c:1402
#define status_print(fmt, args...)
void pe__free_bundle(pcmk_resource_t *rsc)
Definition: bundle.c:2041
bool pcmk__rsc_filtered_by_node(pcmk_resource_t *rsc, GList *only_node)
Definition: utils.c:789
This structure contains everything that makes up a single output formatter.
pcmk_node_t *(* active_node)(const pcmk_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean)
Definition: resources.h:373
pcmk_node_t * allocated_to
Definition: resources.h:447
#define PCMK_XE_DOCKER
Definition: xml_names.h:103
char uname[MAX_NAME]
Definition: cpg.c:53
pe__container_agent
Definition: bundle.c:42
#define PCMK_META_PROMOTED_MAX
Definition: options.h:102
gboolean(* is_filtered)(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: resources.h:358
GList * running_on
Definition: resources.h:456
#define PCMK_XA_HOST_INTERFACE
Definition: xml_names.h:298
gboolean pe__bundle_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: bundle.c:2125
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:520
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:1166
Resource role is unknown.
Definition: roles.h:35
int pe_bundle_replicas(const pcmk_resource_t *rsc)
Get the number of configured replicas in a bundle.
Definition: bundle.c:2089
pcmk_node_t * pe__bundle_active_node(const pcmk_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean)
Definition: bundle.c:2185
#define PCMK_META_ORDERED
Definition: options.h:99
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:883
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:484
const char * parent
Definition: cib.c:27
#define PCMK_XA_RUN_COMMAND
Definition: xml_names.h:392
#define PCMK_XA_SOURCE_DIR_ROOT
Definition: xml_names.h:403
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:770
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition: xml_names.h:122
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2130
int offset
0-origin index of this instance in bundle
void pe__foreach_bundle_replica(pcmk_resource_t *bundle, bool(*fn)(pcmk__bundle_replica_t *, void *), void *user_data)
Definition: bundle.c:208
pcmk_resource_t * remote
Pacemaker Remote connection into container.
#define PCMK_VALUE_REMOTE
Definition: options.h:198
#define PCMK_XA_HOST_NETMASK
Definition: xml_names.h:299
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition: strings.c:713
#define PCMK_XA_UNIQUE
Definition: xml_names.h:434
pe__bundle_mount_flags
Definition: bundle.c:23
uint64_t flags
Definition: remote.c:215
void(* count)(pcmk_resource_t *rsc)
Definition: resources.h:346
pcmk_resource_t * remote_rsc
Definition: nodes.h:136
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:24
pcmk_resource_t * pe__bundled_resource(const pcmk_resource_t *rsc)
Definition: bundle.c:111
GHashTable * allowed_nodes
Definition: resources.h:462
#define PCMK_XA_OPTIONS
Definition: xml_names.h:350
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition: strings.c:993