root/lib/pengine/bundle.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. next_ip
  2. allocate_ip
  3. create_resource
  4. valid_network
  5. create_ip_resource
  6. container_agent_str
  7. create_container_resource
  8. disallow_node
  9. create_remote_resource
  10. create_replica_resources
  11. mount_add
  12. mount_free
  13. port_free
  14. replica_for_remote
  15. pe__bundle_needs_remote_name
  16. pe__add_bundle_remote_name
  17. pe__unpack_bundle
  18. replica_resource_active
  19. pe__bundle_active
  20. pe__find_bundle_replica
  21. print_rsc_in_list
  22. bundle_print_xml
  23. PCMK__OUTPUT_ARGS
  24. pe__bundle_replica_output_html
  25. PCMK__OUTPUT_ARGS
  26. pe__bundle_replica_output_text
  27. PCMK__OUTPUT_ARGS
  28. print_bundle_replica
  29. pe__print_bundle
  30. free_bundle_replica
  31. pe__free_bundle
  32. pe__bundle_resource_state
  33. pe_bundle_replicas
  34. pe__count_bundle
  35. pe__bundle_is_filtered

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

/* [previous][next][first][last][top][bottom][index][help] */