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