LCOV - code coverage report
Current view: top level - pengine - bundle.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 1008 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 46 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2004-2024 the Pacemaker project contributors
       3             :  *
       4             :  * The version control history for this file may have further details.
       5             :  *
       6             :  * This source code is licensed under the GNU Lesser General Public License
       7             :  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <ctype.h>
      13             : #include <stdint.h>
      14             : 
      15             : #include <crm/pengine/rules.h>
      16             : #include <crm/pengine/status.h>
      17             : #include <crm/pengine/internal.h>
      18             : #include <crm/common/xml.h>
      19             : #include <crm/common/output.h>
      20             : #include <crm/common/xml_internal.h>
      21             : #include <pe_status_private.h>
      22             : 
      23             : enum pe__bundle_mount_flags {
      24             :     pe__bundle_mount_none       = 0x00,
      25             : 
      26             :     // mount instance-specific subdirectory rather than source directly
      27             :     pe__bundle_mount_subdir     = 0x01
      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             : 
      42             : enum pe__container_agent {
      43             :     PE__CONTAINER_AGENT_UNKNOWN,
      44             :     PE__CONTAINER_AGENT_DOCKER,
      45             :     PE__CONTAINER_AGENT_RKT,
      46             :     PE__CONTAINER_AGENT_PODMAN,
      47             : };
      48             : 
      49             : #define PE__CONTAINER_AGENT_UNKNOWN_S "unknown"
      50             : #define PE__CONTAINER_AGENT_DOCKER_S  "docker"
      51             : #define PE__CONTAINER_AGENT_RKT_S     "rkt"
      52             : #define PE__CONTAINER_AGENT_PODMAN_S  "podman"
      53             : 
      54             : typedef struct pe__bundle_variant_data_s {
      55             :         int promoted_max;
      56             :         int nreplicas;
      57             :         int nreplicas_per_host;
      58             :         char *prefix;
      59             :         char *image;
      60             :         const char *ip_last;
      61             :         char *host_network;
      62             :         char *host_netmask;
      63             :         char *control_port;
      64             :         char *container_network;
      65             :         char *ip_range_start;
      66             :         gboolean add_host;
      67             :         gchar *container_host_options;
      68             :         char *container_command;
      69             :         char *launcher_options;
      70             :         const char *attribute_target;
      71             : 
      72             :         pcmk_resource_t *child;
      73             : 
      74             :         GList *replicas;    // pcmk__bundle_replica_t *
      75             :         GList *ports;       // pe__bundle_port_t *
      76             :         GList *mounts;      // pe__bundle_mount_t *
      77             : 
      78             :         enum pe__container_agent agent_type;
      79             : } pe__bundle_variant_data_t;
      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             : 
      87             : /*!
      88             :  * \internal
      89             :  * \brief Get maximum number of bundle replicas allowed to run
      90             :  *
      91             :  * \param[in] rsc  Bundle or bundled resource to check
      92             :  *
      93             :  * \return Maximum replicas for bundle corresponding to \p rsc
      94             :  */
      95             : int
      96           0 : pe__bundle_max(const pcmk_resource_t *rsc)
      97             : {
      98           0 :     const pe__bundle_variant_data_t *bundle_data = NULL;
      99             : 
     100           0 :     get_bundle_variant_data(bundle_data, pe__const_top_resource(rsc, true));
     101           0 :     return bundle_data->nreplicas;
     102             : }
     103             : 
     104             : /*!
     105             :  * \internal
     106             :  * \brief Get the resource inside a bundle
     107             :  *
     108             :  * \param[in] bundle  Bundle to check
     109             :  *
     110             :  * \return Resource inside \p bundle if any, otherwise NULL
     111             :  */
     112             : pcmk_resource_t *
     113           0 : pe__bundled_resource(const pcmk_resource_t *rsc)
     114             : {
     115           0 :     const pe__bundle_variant_data_t *bundle_data = NULL;
     116             : 
     117           0 :     get_bundle_variant_data(bundle_data, pe__const_top_resource(rsc, true));
     118           0 :     return bundle_data->child;
     119             : }
     120             : 
     121             : /*!
     122             :  * \internal
     123             :  * \brief Get containerized resource corresponding to a given bundle container
     124             :  *
     125             :  * \param[in] instance  Collective instance that might be a bundle container
     126             :  *
     127             :  * \return Bundled resource instance inside \p instance if it is a bundle
     128             :  *         container instance, otherwise NULL
     129             :  */
     130             : const pcmk_resource_t *
     131           0 : pe__get_rsc_in_container(const pcmk_resource_t *instance)
     132             : {
     133           0 :     const pe__bundle_variant_data_t *data = NULL;
     134           0 :     const pcmk_resource_t *top = pe__const_top_resource(instance, true);
     135             : 
     136           0 :     if ((top == NULL) || (top->variant != pcmk_rsc_variant_bundle)) {
     137           0 :         return NULL;
     138             :     }
     139           0 :     get_bundle_variant_data(data, top);
     140             : 
     141           0 :     for (const GList *iter = data->replicas; iter != NULL; iter = iter->next) {
     142           0 :         const pcmk__bundle_replica_t *replica = iter->data;
     143             : 
     144           0 :         if (instance == replica->container) {
     145           0 :             return replica->child;
     146             :         }
     147             :     }
     148           0 :     return NULL;
     149             : }
     150             : 
     151             : /*!
     152             :  * \internal
     153             :  * \brief Check whether a given node is created by a bundle
     154             :  *
     155             :  * \param[in] bundle  Bundle resource to check
     156             :  * \param[in] node    Node to check
     157             :  *
     158             :  * \return true if \p node is an instance of \p bundle, otherwise false
     159             :  */
     160             : bool
     161           0 : pe__node_is_bundle_instance(const pcmk_resource_t *bundle,
     162             :                             const pcmk_node_t *node)
     163             : {
     164           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
     165             : 
     166           0 :     get_bundle_variant_data(bundle_data, bundle);
     167           0 :     for (GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
     168           0 :         pcmk__bundle_replica_t *replica = iter->data;
     169             : 
     170           0 :         if (pcmk__same_node(node, replica->node)) {
     171           0 :             return true;
     172             :         }
     173             :     }
     174           0 :     return false;
     175             : }
     176             : 
     177             : /*!
     178             :  * \internal
     179             :  * \brief Get the container of a bundle's first replica
     180             :  *
     181             :  * \param[in] bundle  Bundle resource to get container for
     182             :  *
     183             :  * \return Container resource from first replica of \p bundle if any,
     184             :  *         otherwise NULL
     185             :  */
     186             : pcmk_resource_t *
     187           0 : pe__first_container(const pcmk_resource_t *bundle)
     188             : {
     189           0 :     const pe__bundle_variant_data_t *bundle_data = NULL;
     190           0 :     const pcmk__bundle_replica_t *replica = NULL;
     191             : 
     192           0 :     get_bundle_variant_data(bundle_data, bundle);
     193           0 :     if (bundle_data->replicas == NULL) {
     194           0 :         return NULL;
     195             :     }
     196           0 :     replica = bundle_data->replicas->data;
     197           0 :     return replica->container;
     198             : }
     199             : 
     200             : /*!
     201             :  * \internal
     202             :  * \brief Iterate over bundle replicas
     203             :  *
     204             :  * \param[in,out] bundle     Bundle to iterate over
     205             :  * \param[in]     fn         Function to call for each replica (its return value
     206             :  *                           indicates whether to continue iterating)
     207             :  * \param[in,out] user_data  Pointer to pass to \p fn
     208             :  */
     209             : void
     210           0 : pe__foreach_bundle_replica(pcmk_resource_t *bundle,
     211             :                            bool (*fn)(pcmk__bundle_replica_t *, void *),
     212             :                            void *user_data)
     213             : {
     214           0 :     const pe__bundle_variant_data_t *bundle_data = NULL;
     215             : 
     216           0 :     get_bundle_variant_data(bundle_data, bundle);
     217           0 :     for (GList *iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
     218           0 :         if (!fn((pcmk__bundle_replica_t *) iter->data, user_data)) {
     219           0 :             break;
     220             :         }
     221             :     }
     222           0 : }
     223             : 
     224             : /*!
     225             :  * \internal
     226             :  * \brief Iterate over const bundle replicas
     227             :  *
     228             :  * \param[in]     bundle     Bundle to iterate over
     229             :  * \param[in]     fn         Function to call for each replica (its return value
     230             :  *                           indicates whether to continue iterating)
     231             :  * \param[in,out] user_data  Pointer to pass to \p fn
     232             :  */
     233             : void
     234           0 : pe__foreach_const_bundle_replica(const pcmk_resource_t *bundle,
     235             :                                  bool (*fn)(const pcmk__bundle_replica_t *,
     236             :                                             void *),
     237             :                                  void *user_data)
     238             : {
     239           0 :     const pe__bundle_variant_data_t *bundle_data = NULL;
     240             : 
     241           0 :     get_bundle_variant_data(bundle_data, bundle);
     242           0 :     for (const GList *iter = bundle_data->replicas; iter != NULL;
     243           0 :          iter = iter->next) {
     244             : 
     245           0 :         if (!fn((const pcmk__bundle_replica_t *) iter->data, user_data)) {
     246           0 :             break;
     247             :         }
     248             :     }
     249           0 : }
     250             : 
     251             : static char *
     252           0 : next_ip(const char *last_ip)
     253             : {
     254           0 :     unsigned int oct1 = 0;
     255           0 :     unsigned int oct2 = 0;
     256           0 :     unsigned int oct3 = 0;
     257           0 :     unsigned int oct4 = 0;
     258           0 :     int rc = sscanf(last_ip, "%u.%u.%u.%u", &oct1, &oct2, &oct3, &oct4);
     259             : 
     260           0 :     if (rc != 4) {
     261             :         /*@ TODO check for IPv6 */
     262           0 :         return NULL;
     263             : 
     264           0 :     } else if (oct3 > 253) {
     265           0 :         return NULL;
     266             : 
     267           0 :     } else if (oct4 > 253) {
     268           0 :         ++oct3;
     269           0 :         oct4 = 1;
     270             : 
     271             :     } else {
     272           0 :         ++oct4;
     273             :     }
     274             : 
     275           0 :     return crm_strdup_printf("%u.%u.%u.%u", oct1, oct2, oct3, oct4);
     276             : }
     277             : 
     278             : static void
     279           0 : allocate_ip(pe__bundle_variant_data_t *data, pcmk__bundle_replica_t *replica,
     280             :             GString *buffer)
     281             : {
     282           0 :     if(data->ip_range_start == NULL) {
     283           0 :         return;
     284             : 
     285           0 :     } else if(data->ip_last) {
     286           0 :         replica->ipaddr = next_ip(data->ip_last);
     287             : 
     288             :     } else {
     289           0 :         replica->ipaddr = strdup(data->ip_range_start);
     290             :     }
     291             : 
     292           0 :     data->ip_last = replica->ipaddr;
     293           0 :     switch (data->agent_type) {
     294           0 :         case PE__CONTAINER_AGENT_DOCKER:
     295             :         case PE__CONTAINER_AGENT_PODMAN:
     296           0 :             if (data->add_host) {
     297           0 :                 g_string_append_printf(buffer, " --add-host=%s-%d:%s",
     298             :                                        data->prefix, replica->offset,
     299             :                                        replica->ipaddr);
     300             :             } else {
     301           0 :                 g_string_append_printf(buffer, " --hosts-entry=%s=%s-%d",
     302             :                                        replica->ipaddr, data->prefix,
     303             :                                        replica->offset);
     304             :             }
     305           0 :             break;
     306             : 
     307           0 :         case PE__CONTAINER_AGENT_RKT:
     308           0 :             g_string_append_printf(buffer, " --hosts-entry=%s=%s-%d",
     309             :                                    replica->ipaddr, data->prefix,
     310             :                                    replica->offset);
     311           0 :             break;
     312             : 
     313           0 :         default: // PE__CONTAINER_AGENT_UNKNOWN
     314           0 :             break;
     315             :     }
     316             : }
     317             : 
     318             : static xmlNode *
     319           0 : create_resource(const char *name, const char *provider, const char *kind)
     320             : {
     321           0 :     xmlNode *rsc = pcmk__xe_create(NULL, PCMK_XE_PRIMITIVE);
     322             : 
     323           0 :     crm_xml_add(rsc, PCMK_XA_ID, name);
     324           0 :     crm_xml_add(rsc, PCMK_XA_CLASS, PCMK_RESOURCE_CLASS_OCF);
     325           0 :     crm_xml_add(rsc, PCMK_XA_PROVIDER, provider);
     326           0 :     crm_xml_add(rsc, PCMK_XA_TYPE, kind);
     327             : 
     328           0 :     return rsc;
     329             : }
     330             : 
     331             : /*!
     332             :  * \internal
     333             :  * \brief Check whether cluster can manage resource inside container
     334             :  *
     335             :  * \param[in,out] data  Container variant data
     336             :  *
     337             :  * \return TRUE if networking configuration is acceptable, FALSE otherwise
     338             :  *
     339             :  * \note The resource is manageable if an IP range or control port has been
     340             :  *       specified. If a control port is used without an IP range, replicas per
     341             :  *       host must be 1.
     342             :  */
     343             : static bool
     344           0 : valid_network(pe__bundle_variant_data_t *data)
     345             : {
     346           0 :     if(data->ip_range_start) {
     347           0 :         return TRUE;
     348             :     }
     349           0 :     if(data->control_port) {
     350           0 :         if(data->nreplicas_per_host > 1) {
     351           0 :             pcmk__config_err("Specifying the '" PCMK_XA_CONTROL_PORT "' for %s "
     352             :                              "requires '" PCMK_XA_REPLICAS_PER_HOST "=1'",
     353             :                              data->prefix);
     354           0 :             data->nreplicas_per_host = 1;
     355             :             // @TODO to be sure:
     356             :             // pcmk__clear_rsc_flags(rsc, pcmk_rsc_unique);
     357             :         }
     358           0 :         return TRUE;
     359             :     }
     360           0 :     return FALSE;
     361             : }
     362             : 
     363             : static int
     364           0 : create_ip_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data,
     365             :                    pcmk__bundle_replica_t *replica)
     366             : {
     367           0 :     if(data->ip_range_start) {
     368           0 :         char *id = NULL;
     369           0 :         xmlNode *xml_ip = NULL;
     370           0 :         xmlNode *xml_obj = NULL;
     371             : 
     372           0 :         id = crm_strdup_printf("%s-ip-%s", data->prefix, replica->ipaddr);
     373           0 :         crm_xml_sanitize_id(id);
     374           0 :         xml_ip = create_resource(id, "heartbeat", "IPaddr2");
     375           0 :         free(id);
     376             : 
     377           0 :         xml_obj = pcmk__xe_create(xml_ip, PCMK_XE_INSTANCE_ATTRIBUTES);
     378           0 :         crm_xml_set_id(xml_obj, "%s-attributes-%d",
     379             :                        data->prefix, replica->offset);
     380             : 
     381           0 :         crm_create_nvpair_xml(xml_obj, NULL, "ip", replica->ipaddr);
     382           0 :         if(data->host_network) {
     383           0 :             crm_create_nvpair_xml(xml_obj, NULL, "nic", data->host_network);
     384             :         }
     385             : 
     386           0 :         if(data->host_netmask) {
     387           0 :             crm_create_nvpair_xml(xml_obj, NULL,
     388           0 :                                   "cidr_netmask", data->host_netmask);
     389             : 
     390             :         } else {
     391           0 :             crm_create_nvpair_xml(xml_obj, NULL, "cidr_netmask", "32");
     392             :         }
     393             : 
     394           0 :         xml_obj = pcmk__xe_create(xml_ip, PCMK_XE_OPERATIONS);
     395           0 :         crm_create_op_xml(xml_obj, pcmk__xe_id(xml_ip), PCMK_ACTION_MONITOR,
     396             :                           "60s", NULL);
     397             : 
     398             :         // TODO: Other ops? Timeouts and intervals from underlying resource?
     399             : 
     400           0 :         if (pe__unpack_resource(xml_ip, &replica->ip, parent,
     401             :                                 parent->cluster) != pcmk_rc_ok) {
     402           0 :             return pcmk_rc_unpack_error;
     403             :         }
     404             : 
     405           0 :         parent->children = g_list_append(parent->children, replica->ip);
     406             :     }
     407           0 :     return pcmk_rc_ok;
     408             : }
     409             : 
     410             : static const char*
     411           0 : container_agent_str(enum pe__container_agent t)
     412             : {
     413           0 :     switch (t) {
     414           0 :         case PE__CONTAINER_AGENT_DOCKER: return PE__CONTAINER_AGENT_DOCKER_S;
     415           0 :         case PE__CONTAINER_AGENT_RKT:    return PE__CONTAINER_AGENT_RKT_S;
     416           0 :         case PE__CONTAINER_AGENT_PODMAN: return PE__CONTAINER_AGENT_PODMAN_S;
     417           0 :         default: // PE__CONTAINER_AGENT_UNKNOWN
     418           0 :             break;
     419             :     }
     420           0 :     return PE__CONTAINER_AGENT_UNKNOWN_S;
     421             : }
     422             : 
     423             : static int
     424           0 : create_container_resource(pcmk_resource_t *parent,
     425             :                           const pe__bundle_variant_data_t *data,
     426             :                           pcmk__bundle_replica_t *replica)
     427             : {
     428           0 :     char *id = NULL;
     429           0 :     xmlNode *xml_container = NULL;
     430           0 :     xmlNode *xml_obj = NULL;
     431             : 
     432             :     // Agent-specific
     433           0 :     const char *hostname_opt = NULL;
     434           0 :     const char *env_opt = NULL;
     435           0 :     const char *agent_str = NULL;
     436           0 :     int volid = 0;  // rkt-only
     437             : 
     438           0 :     GString *buffer = NULL;
     439           0 :     GString *dbuffer = NULL;
     440             : 
     441             :     // Where syntax differences are drop-in replacements, set them now
     442           0 :     switch (data->agent_type) {
     443           0 :         case PE__CONTAINER_AGENT_DOCKER:
     444             :         case PE__CONTAINER_AGENT_PODMAN:
     445           0 :             hostname_opt = "-h ";
     446           0 :             env_opt = "-e ";
     447           0 :             break;
     448           0 :         case PE__CONTAINER_AGENT_RKT:
     449           0 :             hostname_opt = "--hostname=";
     450           0 :             env_opt = "--environment=";
     451           0 :             break;
     452           0 :         default:    // PE__CONTAINER_AGENT_UNKNOWN
     453           0 :             return pcmk_rc_unpack_error;
     454             :     }
     455           0 :     agent_str = container_agent_str(data->agent_type);
     456             : 
     457           0 :     buffer = g_string_sized_new(4096);
     458             : 
     459           0 :     id = crm_strdup_printf("%s-%s-%d", data->prefix, agent_str,
     460             :                            replica->offset);
     461           0 :     crm_xml_sanitize_id(id);
     462           0 :     xml_container = create_resource(id, "heartbeat", agent_str);
     463           0 :     free(id);
     464             : 
     465           0 :     xml_obj = pcmk__xe_create(xml_container, PCMK_XE_INSTANCE_ATTRIBUTES);
     466           0 :     crm_xml_set_id(xml_obj, "%s-attributes-%d", data->prefix, replica->offset);
     467             : 
     468           0 :     crm_create_nvpair_xml(xml_obj, NULL, "image", data->image);
     469           0 :     crm_create_nvpair_xml(xml_obj, NULL, "allow_pull", PCMK_VALUE_TRUE);
     470           0 :     crm_create_nvpair_xml(xml_obj, NULL, "force_kill", PCMK_VALUE_FALSE);
     471           0 :     crm_create_nvpair_xml(xml_obj, NULL, "reuse", PCMK_VALUE_FALSE);
     472             : 
     473           0 :     if (data->agent_type == PE__CONTAINER_AGENT_DOCKER) {
     474           0 :         g_string_append(buffer, " --restart=no");
     475             :     }
     476             : 
     477             :     /* Set a container hostname only if we have an IP to map it to. The user can
     478             :      * set -h or --uts=host themselves if they want a nicer name for logs, but
     479             :      * this makes applications happy who need their  hostname to match the IP
     480             :      * they bind to.
     481             :      */
     482           0 :     if (data->ip_range_start != NULL) {
     483           0 :         g_string_append_printf(buffer, " %s%s-%d", hostname_opt, data->prefix,
     484             :                                replica->offset);
     485             :     }
     486           0 :     pcmk__g_strcat(buffer, " ", env_opt, "PCMK_stderr=1", NULL);
     487             : 
     488           0 :     if (data->container_network != NULL) {
     489           0 :         pcmk__g_strcat(buffer, " --net=", data->container_network, NULL);
     490             :     }
     491             : 
     492           0 :     if (data->control_port != NULL) {
     493           0 :         pcmk__g_strcat(buffer, " ", env_opt, "PCMK_" PCMK__ENV_REMOTE_PORT "=",
     494           0 :                        data->control_port, NULL);
     495             :     } else {
     496           0 :         g_string_append_printf(buffer, " %sPCMK_" PCMK__ENV_REMOTE_PORT "=%d",
     497             :                                env_opt, DEFAULT_REMOTE_PORT);
     498             :     }
     499             : 
     500           0 :     for (GList *iter = data->mounts; iter != NULL; iter = iter->next) {
     501           0 :         pe__bundle_mount_t *mount = (pe__bundle_mount_t *) iter->data;
     502           0 :         char *source = NULL;
     503             : 
     504           0 :         if (pcmk_is_set(mount->flags, pe__bundle_mount_subdir)) {
     505           0 :             source = crm_strdup_printf("%s/%s-%d", mount->source, data->prefix,
     506             :                                        replica->offset);
     507           0 :             pcmk__add_separated_word(&dbuffer, 1024, source, ",");
     508             :         }
     509             : 
     510           0 :         switch (data->agent_type) {
     511           0 :             case PE__CONTAINER_AGENT_DOCKER:
     512             :             case PE__CONTAINER_AGENT_PODMAN:
     513           0 :                 pcmk__g_strcat(buffer,
     514           0 :                                " -v ", pcmk__s(source, mount->source),
     515             :                                ":", mount->target, NULL);
     516             : 
     517           0 :                 if (mount->options != NULL) {
     518           0 :                     pcmk__g_strcat(buffer, ":", mount->options, NULL);
     519             :                 }
     520           0 :                 break;
     521           0 :             case PE__CONTAINER_AGENT_RKT:
     522           0 :                 g_string_append_printf(buffer,
     523             :                                        " --volume vol%d,kind=host,"
     524             :                                        "source=%s%s%s "
     525             :                                        "--mount volume=vol%d,target=%s",
     526           0 :                                        volid, pcmk__s(source, mount->source),
     527           0 :                                        (mount->options != NULL)? "," : "",
     528           0 :                                        pcmk__s(mount->options, ""),
     529             :                                        volid, mount->target);
     530           0 :                 volid++;
     531           0 :                 break;
     532           0 :             default:
     533           0 :                 break;
     534             :         }
     535           0 :         free(source);
     536             :     }
     537             : 
     538           0 :     for (GList *iter = data->ports; iter != NULL; iter = iter->next) {
     539           0 :         pe__bundle_port_t *port = (pe__bundle_port_t *) iter->data;
     540             : 
     541           0 :         switch (data->agent_type) {
     542           0 :             case PE__CONTAINER_AGENT_DOCKER:
     543             :             case PE__CONTAINER_AGENT_PODMAN:
     544           0 :                 if (replica->ipaddr != NULL) {
     545           0 :                     pcmk__g_strcat(buffer,
     546             :                                    " -p ", replica->ipaddr, ":", port->source,
     547             :                                    ":", port->target, NULL);
     548             : 
     549           0 :                 } else if (!pcmk__str_eq(data->container_network,
     550             :                                          PCMK_VALUE_HOST, pcmk__str_none)) {
     551             :                     // No need to do port mapping if net == host
     552           0 :                     pcmk__g_strcat(buffer,
     553             :                                    " -p ", port->source, ":", port->target,
     554             :                                    NULL);
     555             :                 }
     556           0 :                 break;
     557           0 :             case PE__CONTAINER_AGENT_RKT:
     558           0 :                 if (replica->ipaddr != NULL) {
     559           0 :                     pcmk__g_strcat(buffer,
     560             :                                    " --port=", port->target,
     561             :                                    ":", replica->ipaddr, ":", port->source,
     562             :                                    NULL);
     563             :                 } else {
     564           0 :                     pcmk__g_strcat(buffer,
     565             :                                    " --port=", port->target, ":", port->source,
     566             :                                    NULL);
     567             :                 }
     568           0 :                 break;
     569           0 :             default:
     570           0 :                 break;
     571             :         }
     572             :     }
     573             : 
     574             :     /* @COMPAT: We should use pcmk__add_word() here, but we can't yet, because
     575             :      * it would cause restarts during rolling upgrades.
     576             :      *
     577             :      * In a previous version of the container resource creation logic, if
     578             :      * data->launcher_options is not NULL, we append
     579             :      * (" %s", data->launcher_options) even if data->launcher_options is an
     580             :      * empty string. Likewise for data->container_host_options. Using
     581             :      *
     582             :      *     pcmk__add_word(buffer, 0, data->launcher_options)
     583             :      *
     584             :      * removes that extra trailing space, causing a resource definition change.
     585             :      */
     586           0 :     if (data->launcher_options != NULL) {
     587           0 :         pcmk__g_strcat(buffer, " ", data->launcher_options, NULL);
     588             :     }
     589             : 
     590           0 :     if (data->container_host_options != NULL) {
     591           0 :         pcmk__g_strcat(buffer, " ", data->container_host_options, NULL);
     592             :     }
     593             : 
     594           0 :     crm_create_nvpair_xml(xml_obj, NULL, "run_opts",
     595           0 :                           (const char *) buffer->str);
     596           0 :     g_string_free(buffer, TRUE);
     597             : 
     598           0 :     crm_create_nvpair_xml(xml_obj, NULL, "mount_points",
     599           0 :                           (dbuffer != NULL)? (const char *) dbuffer->str : "");
     600           0 :     if (dbuffer != NULL) {
     601           0 :         g_string_free(dbuffer, TRUE);
     602             :     }
     603             : 
     604           0 :     if (replica->child != NULL) {
     605           0 :         if (data->container_command != NULL) {
     606           0 :             crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
     607           0 :                                   data->container_command);
     608             :         } else {
     609           0 :             crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
     610             :                                   SBIN_DIR "/pacemaker-remoted");
     611             :         }
     612             : 
     613             :         /* TODO: Allow users to specify their own?
     614             :          *
     615             :          * We just want to know if the container is alive; we'll monitor the
     616             :          * child independently.
     617             :          */
     618           0 :         crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true");
     619             : #if 0
     620             :         /* @TODO Consider supporting the use case where we can start and stop
     621             :          * resources, but not proxy local commands (such as setting node
     622             :          * attributes), by running the local executor in stand-alone mode.
     623             :          * However, this would probably be better done via ACLs as with other
     624             :          * Pacemaker Remote nodes.
     625             :          */
     626             :     } else if ((child != NULL) && data->untrusted) {
     627             :         crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
     628             :                               CRM_DAEMON_DIR "/pacemaker-execd");
     629             :         crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd",
     630             :                               CRM_DAEMON_DIR "/pacemaker/cts-exec-helper -c poke");
     631             : #endif
     632             :     } else {
     633           0 :         if (data->container_command != NULL) {
     634           0 :             crm_create_nvpair_xml(xml_obj, NULL, "run_cmd",
     635           0 :                                   data->container_command);
     636             :         }
     637             : 
     638             :         /* TODO: Allow users to specify their own?
     639             :          *
     640             :          * We don't know what's in the container, so we just want to know if it
     641             :          * is alive.
     642             :          */
     643           0 :         crm_create_nvpair_xml(xml_obj, NULL, "monitor_cmd", "/bin/true");
     644             :     }
     645             : 
     646           0 :     xml_obj = pcmk__xe_create(xml_container, PCMK_XE_OPERATIONS);
     647           0 :     crm_create_op_xml(xml_obj, pcmk__xe_id(xml_container), PCMK_ACTION_MONITOR,
     648             :                       "60s", NULL);
     649             : 
     650             :     // TODO: Other ops? Timeouts and intervals from underlying resource?
     651           0 :     if (pe__unpack_resource(xml_container, &replica->container, parent,
     652             :                             parent->cluster) != pcmk_rc_ok) {
     653           0 :         return pcmk_rc_unpack_error;
     654             :     }
     655           0 :     pcmk__set_rsc_flags(replica->container, pcmk_rsc_replica_container);
     656           0 :     parent->children = g_list_append(parent->children, replica->container);
     657             : 
     658           0 :     return pcmk_rc_ok;
     659             : }
     660             : 
     661             : /*!
     662             :  * \brief Ban a node from a resource's (and its children's) allowed nodes list
     663             :  *
     664             :  * \param[in,out] rsc    Resource to modify
     665             :  * \param[in]     uname  Name of node to ban
     666             :  */
     667             : static void
     668           0 : disallow_node(pcmk_resource_t *rsc, const char *uname)
     669             : {
     670           0 :     gpointer match = g_hash_table_lookup(rsc->allowed_nodes, uname);
     671             : 
     672           0 :     if (match) {
     673           0 :         ((pcmk_node_t *) match)->weight = -PCMK_SCORE_INFINITY;
     674           0 :         ((pcmk_node_t *) match)->rsc_discover_mode = pcmk_probe_never;
     675             :     }
     676           0 :     if (rsc->children) {
     677           0 :         g_list_foreach(rsc->children, (GFunc) disallow_node, (gpointer) uname);
     678             :     }
     679           0 : }
     680             : 
     681             : static int
     682           0 : create_remote_resource(pcmk_resource_t *parent, pe__bundle_variant_data_t *data,
     683             :                        pcmk__bundle_replica_t *replica)
     684             : {
     685           0 :     if (replica->child && valid_network(data)) {
     686             :         GHashTableIter gIter;
     687           0 :         pcmk_node_t *node = NULL;
     688           0 :         xmlNode *xml_remote = NULL;
     689           0 :         char *id = crm_strdup_printf("%s-%d", data->prefix, replica->offset);
     690           0 :         char *port_s = NULL;
     691           0 :         const char *uname = NULL;
     692           0 :         const char *connect_name = NULL;
     693             : 
     694           0 :         if (pe_find_resource(parent->cluster->resources, id) != NULL) {
     695           0 :             free(id);
     696             :             // The biggest hammer we have
     697           0 :             id = crm_strdup_printf("pcmk-internal-%s-remote-%d",
     698           0 :                                    replica->child->id, replica->offset);
     699             :             //@TODO return error instead of asserting?
     700           0 :             CRM_ASSERT(pe_find_resource(parent->cluster->resources,
     701             :                                         id) == NULL);
     702             :         }
     703             : 
     704             :         /* REMOTE_CONTAINER_HACK: Using "#uname" as the server name when the
     705             :          * connection does not have its own IP is a magic string that we use to
     706             :          * support nested remotes (i.e. a bundle running on a remote node).
     707             :          */
     708           0 :         connect_name = (replica->ipaddr? replica->ipaddr : "#uname");
     709             : 
     710           0 :         if (data->control_port == NULL) {
     711           0 :             port_s = pcmk__itoa(DEFAULT_REMOTE_PORT);
     712             :         }
     713             : 
     714             :         /* This sets replica->container as replica->remote's container, which is
     715             :          * similar to what happens with guest nodes. This is how the scheduler
     716             :          * knows that the bundle node is fenced by recovering the container, and
     717             :          * that remote should be ordered relative to the container.
     718             :          */
     719           0 :         xml_remote = pe_create_remote_xml(NULL, id, replica->container->id,
     720             :                                           NULL, NULL, NULL,
     721           0 :                                           connect_name, (data->control_port?
     722             :                                           data->control_port : port_s));
     723           0 :         free(port_s);
     724             : 
     725             :         /* Abandon our created ID, and pull the copy from the XML, because we
     726             :          * need something that will get freed during scheduler data cleanup to
     727             :          * use as the node ID and uname.
     728             :          */
     729           0 :         free(id);
     730           0 :         id = NULL;
     731           0 :         uname = pcmk__xe_id(xml_remote);
     732             : 
     733             :         /* Ensure a node has been created for the guest (it may have already
     734             :          * been, if it has a permanent node attribute), and ensure its weight is
     735             :          * -INFINITY so no other resources can run on it.
     736             :          */
     737           0 :         node = pcmk_find_node(parent->cluster, uname);
     738           0 :         if (node == NULL) {
     739           0 :             node = pe_create_node(uname, uname, PCMK_VALUE_REMOTE,
     740             :                                   PCMK_VALUE_MINUS_INFINITY, parent->cluster);
     741             :         } else {
     742           0 :             node->weight = -PCMK_SCORE_INFINITY;
     743             :         }
     744           0 :         node->rsc_discover_mode = pcmk_probe_never;
     745             : 
     746             :         /* unpack_remote_nodes() ensures that each remote node and guest node
     747             :          * has a pcmk_node_t entry. Ideally, it would do the same for bundle
     748             :          * nodes. Unfortunately, a bundle has to be mostly unpacked before it's
     749             :          * obvious what nodes will be needed, so we do it just above.
     750             :          *
     751             :          * Worse, that means that the node may have been utilized while
     752             :          * unpacking other resources, without our weight correction. The most
     753             :          * likely place for this to happen is when pe__unpack_resource() calls
     754             :          * resource_location() to set a default score in symmetric clusters.
     755             :          * This adds a node *copy* to each resource's allowed nodes, and these
     756             :          * copies will have the wrong weight.
     757             :          *
     758             :          * As a hacky workaround, fix those copies here.
     759             :          *
     760             :          * @TODO Possible alternative: ensure bundles are unpacked before other
     761             :          * resources, so the weight is correct before any copies are made.
     762             :          */
     763           0 :         g_list_foreach(parent->cluster->resources, (GFunc) disallow_node,
     764             :                        (gpointer) uname);
     765             : 
     766           0 :         replica->node = pe__copy_node(node);
     767           0 :         replica->node->weight = 500;
     768           0 :         replica->node->rsc_discover_mode = pcmk_probe_exclusive;
     769             : 
     770             :         /* Ensure the node shows up as allowed and with the correct discovery set */
     771           0 :         if (replica->child->allowed_nodes != NULL) {
     772           0 :             g_hash_table_destroy(replica->child->allowed_nodes);
     773             :         }
     774           0 :         replica->child->allowed_nodes = pcmk__strkey_table(NULL, free);
     775           0 :         g_hash_table_insert(replica->child->allowed_nodes,
     776           0 :                             (gpointer) replica->node->details->id,
     777           0 :                             pe__copy_node(replica->node));
     778             : 
     779             :         {
     780           0 :             pcmk_node_t *copy = pe__copy_node(replica->node);
     781           0 :             copy->weight = -PCMK_SCORE_INFINITY;
     782           0 :             g_hash_table_insert(replica->child->parent->allowed_nodes,
     783           0 :                                 (gpointer) replica->node->details->id, copy);
     784             :         }
     785           0 :         if (pe__unpack_resource(xml_remote, &replica->remote, parent,
     786             :                                 parent->cluster) != pcmk_rc_ok) {
     787           0 :             return pcmk_rc_unpack_error;
     788             :         }
     789             : 
     790           0 :         g_hash_table_iter_init(&gIter, replica->remote->allowed_nodes);
     791           0 :         while (g_hash_table_iter_next(&gIter, NULL, (void **)&node)) {
     792           0 :             if (pcmk__is_pacemaker_remote_node(node)) {
     793             :                 /* Remote resources can only run on 'normal' cluster node */
     794           0 :                 node->weight = -PCMK_SCORE_INFINITY;
     795             :             }
     796             :         }
     797             : 
     798           0 :         replica->node->details->remote_rsc = replica->remote;
     799             : 
     800             :         // Ensure pcmk__is_guest_or_bundle_node() functions correctly
     801           0 :         replica->remote->container = replica->container;
     802             : 
     803             :         /* A bundle's #kind is closer to "container" (guest node) than the
     804             :          * "remote" set by pe_create_node().
     805             :          */
     806           0 :         pcmk__insert_dup(replica->node->details->attrs,
     807             :                          CRM_ATTR_KIND, "container");
     808             : 
     809             :         /* One effect of this is that setup_container() will add
     810             :          * replica->remote to replica->container's fillers, which will make
     811             :          * pe__resource_contains_guest_node() true for replica->container.
     812             :          *
     813             :          * replica->child does NOT get added to replica->container's fillers.
     814             :          * The only noticeable effect if it did would be for its fail count to
     815             :          * be taken into account when checking replica->container's migration
     816             :          * threshold.
     817             :          */
     818           0 :         parent->children = g_list_append(parent->children, replica->remote);
     819             :     }
     820           0 :     return pcmk_rc_ok;
     821             : }
     822             : 
     823             : static int
     824           0 : create_replica_resources(pcmk_resource_t *parent,
     825             :                          pe__bundle_variant_data_t *data,
     826             :                          pcmk__bundle_replica_t *replica)
     827             : {
     828           0 :     int rc = pcmk_rc_ok;
     829             : 
     830           0 :     rc = create_container_resource(parent, data, replica);
     831           0 :     if (rc != pcmk_rc_ok) {
     832           0 :         return rc;
     833             :     }
     834             : 
     835           0 :     rc = create_ip_resource(parent, data, replica);
     836           0 :     if (rc != pcmk_rc_ok) {
     837           0 :         return rc;
     838             :     }
     839             : 
     840           0 :     rc = create_remote_resource(parent, data, replica);
     841           0 :     if (rc != pcmk_rc_ok) {
     842           0 :         return rc;
     843             :     }
     844             : 
     845           0 :     if ((replica->child != NULL) && (replica->ipaddr != NULL)) {
     846           0 :         pcmk__insert_meta(replica->child, "external-ip", replica->ipaddr);
     847             :     }
     848             : 
     849           0 :     if (replica->remote != NULL) {
     850             :         /*
     851             :          * Allow the remote connection resource to be allocated to a
     852             :          * different node than the one on which the container is active.
     853             :          *
     854             :          * This makes it possible to have Pacemaker Remote nodes running
     855             :          * containers with pacemaker-remoted inside in order to start
     856             :          * services inside those containers.
     857             :          */
     858           0 :         pcmk__set_rsc_flags(replica->remote, pcmk_rsc_remote_nesting_allowed);
     859             :     }
     860           0 :     return rc;
     861             : }
     862             : 
     863             : static void
     864           0 : mount_add(pe__bundle_variant_data_t *bundle_data, const char *source,
     865             :           const char *target, const char *options, uint32_t flags)
     866             : {
     867           0 :     pe__bundle_mount_t *mount = pcmk__assert_alloc(1,
     868             :                                                    sizeof(pe__bundle_mount_t));
     869             : 
     870           0 :     mount->source = pcmk__str_copy(source);
     871           0 :     mount->target = pcmk__str_copy(target);
     872           0 :     mount->options = pcmk__str_copy(options);
     873           0 :     mount->flags = flags;
     874           0 :     bundle_data->mounts = g_list_append(bundle_data->mounts, mount);
     875           0 : }
     876             : 
     877             : static void
     878           0 : mount_free(pe__bundle_mount_t *mount)
     879             : {
     880           0 :     free(mount->source);
     881           0 :     free(mount->target);
     882           0 :     free(mount->options);
     883           0 :     free(mount);
     884           0 : }
     885             : 
     886             : static void
     887           0 : port_free(pe__bundle_port_t *port)
     888             : {
     889           0 :     free(port->source);
     890           0 :     free(port->target);
     891           0 :     free(port);
     892           0 : }
     893             : 
     894             : static pcmk__bundle_replica_t *
     895           0 : replica_for_remote(pcmk_resource_t *remote)
     896             : {
     897           0 :     pcmk_resource_t *top = remote;
     898           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
     899             : 
     900           0 :     if (top == NULL) {
     901           0 :         return NULL;
     902             :     }
     903             : 
     904           0 :     while (top->parent != NULL) {
     905           0 :         top = top->parent;
     906             :     }
     907             : 
     908           0 :     get_bundle_variant_data(bundle_data, top);
     909           0 :     for (GList *gIter = bundle_data->replicas; gIter != NULL;
     910           0 :          gIter = gIter->next) {
     911           0 :         pcmk__bundle_replica_t *replica = gIter->data;
     912             : 
     913           0 :         if (replica->remote == remote) {
     914           0 :             return replica;
     915             :         }
     916             :     }
     917           0 :     CRM_LOG_ASSERT(FALSE);
     918           0 :     return NULL;
     919             : }
     920             : 
     921             : bool
     922           0 : pe__bundle_needs_remote_name(pcmk_resource_t *rsc)
     923             : {
     924             :     const char *value;
     925           0 :     GHashTable *params = NULL;
     926             : 
     927           0 :     if (rsc == NULL) {
     928           0 :         return false;
     929             :     }
     930             : 
     931             :     // Use NULL node since pcmk__bundle_expand() uses that to set value
     932           0 :     params = pe_rsc_params(rsc, NULL, rsc->cluster);
     933           0 :     value = g_hash_table_lookup(params, PCMK_REMOTE_RA_ADDR);
     934             : 
     935           0 :     return pcmk__str_eq(value, "#uname", pcmk__str_casei)
     936           0 :            && xml_contains_remote_node(rsc->xml);
     937             : }
     938             : 
     939             : const char *
     940           0 : pe__add_bundle_remote_name(pcmk_resource_t *rsc, xmlNode *xml,
     941             :                            const char *field)
     942             : {
     943             :     // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
     944             : 
     945           0 :     pcmk_node_t *node = NULL;
     946           0 :     pcmk__bundle_replica_t *replica = NULL;
     947             : 
     948           0 :     if (!pe__bundle_needs_remote_name(rsc)) {
     949           0 :         return NULL;
     950             :     }
     951             : 
     952           0 :     replica = replica_for_remote(rsc);
     953           0 :     if (replica == NULL) {
     954           0 :         return NULL;
     955             :     }
     956             : 
     957           0 :     node = replica->container->allocated_to;
     958           0 :     if (node == NULL) {
     959             :         /* If it won't be running anywhere after the
     960             :          * transition, go with where it's running now.
     961             :          */
     962           0 :         node = pcmk__current_node(replica->container);
     963             :     }
     964             : 
     965           0 :     if(node == NULL) {
     966           0 :         crm_trace("Cannot determine address for bundle connection %s", rsc->id);
     967           0 :         return NULL;
     968             :     }
     969             : 
     970           0 :     crm_trace("Setting address for bundle connection %s to bundle host %s",
     971             :               rsc->id, pcmk__node_name(node));
     972           0 :     if(xml != NULL && field != NULL) {
     973           0 :         crm_xml_add(xml, field, node->details->uname);
     974             :     }
     975             : 
     976           0 :     return node->details->uname;
     977             : }
     978             : 
     979             : #define pe__set_bundle_mount_flags(mount_xml, flags, flags_to_set) do {     \
     980             :         flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,           \
     981             :                                    "Bundle mount", pcmk__xe_id(mount_xml),  \
     982             :                                    flags, (flags_to_set), #flags_to_set);   \
     983             :     } while (0)
     984             : 
     985             : gboolean
     986           0 : pe__unpack_bundle(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
     987             : {
     988           0 :     const char *value = NULL;
     989           0 :     xmlNode *xml_obj = NULL;
     990           0 :     const xmlNode *xml_child = NULL;
     991           0 :     xmlNode *xml_resource = NULL;
     992           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
     993           0 :     bool need_log_mount = TRUE;
     994             : 
     995           0 :     CRM_ASSERT(rsc != NULL);
     996           0 :     pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
     997             : 
     998           0 :     bundle_data = pcmk__assert_alloc(1, sizeof(pe__bundle_variant_data_t));
     999           0 :     rsc->variant_opaque = bundle_data;
    1000           0 :     bundle_data->prefix = strdup(rsc->id);
    1001             : 
    1002           0 :     xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_DOCKER, NULL, NULL);
    1003           0 :     if (xml_obj != NULL) {
    1004           0 :         bundle_data->agent_type = PE__CONTAINER_AGENT_DOCKER;
    1005             :     } else {
    1006           0 :         xml_obj = pcmk__xe_first_child(rsc->xml, PCMK__XE_RKT, NULL, NULL);
    1007           0 :         if (xml_obj != NULL) {
    1008           0 :             pcmk__warn_once(pcmk__wo_rkt,
    1009             :                             "Support for " PCMK__XE_RKT " in bundles "
    1010             :                             "(such as %s) is deprecated and will be "
    1011             :                             "removed in a future release", rsc->id);
    1012           0 :             bundle_data->agent_type = PE__CONTAINER_AGENT_RKT;
    1013             :         } else {
    1014           0 :             xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_PODMAN, NULL,
    1015             :                                            NULL);
    1016           0 :             if (xml_obj != NULL) {
    1017           0 :                 bundle_data->agent_type = PE__CONTAINER_AGENT_PODMAN;
    1018             :             } else {
    1019           0 :                 return FALSE;
    1020             :             }
    1021             :         }
    1022             :     }
    1023             : 
    1024             :     // Use 0 for default, minimum, and invalid PCMK_XA_PROMOTED_MAX
    1025           0 :     value = crm_element_value(xml_obj, PCMK_XA_PROMOTED_MAX);
    1026           0 :     if (value == NULL) {
    1027             :         // @COMPAT deprecated since 2.0.0
    1028           0 :         value = crm_element_value(xml_obj, PCMK__XA_PROMOTED_MAX_LEGACY);
    1029             : 
    1030           0 :         if (value != NULL) {
    1031           0 :             pcmk__warn_once(pcmk__wo_bundle_master,
    1032             :                             "Support for the " PCMK__XA_PROMOTED_MAX_LEGACY
    1033             :                             " attribute (such as in %s) is deprecated and "
    1034             :                             "will be removed in a future release. Use "
    1035             :                             PCMK_XA_PROMOTED_MAX " instead.",
    1036             :                             rsc->id);
    1037             :         }
    1038             :     }
    1039           0 :     pcmk__scan_min_int(value, &bundle_data->promoted_max, 0);
    1040             : 
    1041             :     /* Default replicas to PCMK_XA_PROMOTED_MAX if it was specified and 1
    1042             :      * otherwise
    1043             :      */
    1044           0 :     value = crm_element_value(xml_obj, PCMK_XA_REPLICAS);
    1045           0 :     if ((value == NULL) && (bundle_data->promoted_max > 0)) {
    1046           0 :         bundle_data->nreplicas = bundle_data->promoted_max;
    1047             :     } else {
    1048           0 :         pcmk__scan_min_int(value, &bundle_data->nreplicas, 1);
    1049             :     }
    1050             : 
    1051             :     /*
    1052             :      * Communication between containers on the same host via the
    1053             :      * floating IPs only works if the container is started with:
    1054             :      *   --userland-proxy=false --ip-masq=false
    1055             :      */
    1056           0 :     value = crm_element_value(xml_obj, PCMK_XA_REPLICAS_PER_HOST);
    1057           0 :     pcmk__scan_min_int(value, &bundle_data->nreplicas_per_host, 1);
    1058           0 :     if (bundle_data->nreplicas_per_host == 1) {
    1059           0 :         pcmk__clear_rsc_flags(rsc, pcmk_rsc_unique);
    1060             :     }
    1061             : 
    1062           0 :     bundle_data->container_command =
    1063           0 :         crm_element_value_copy(xml_obj, PCMK_XA_RUN_COMMAND);
    1064           0 :     bundle_data->launcher_options = crm_element_value_copy(xml_obj,
    1065             :                                                            PCMK_XA_OPTIONS);
    1066           0 :     bundle_data->image = crm_element_value_copy(xml_obj, PCMK_XA_IMAGE);
    1067           0 :     bundle_data->container_network = crm_element_value_copy(xml_obj,
    1068             :                                                             PCMK_XA_NETWORK);
    1069             : 
    1070           0 :     xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_NETWORK, NULL, NULL);
    1071           0 :     if(xml_obj) {
    1072           0 :         bundle_data->ip_range_start =
    1073           0 :             crm_element_value_copy(xml_obj, PCMK_XA_IP_RANGE_START);
    1074           0 :         bundle_data->host_netmask =
    1075           0 :             crm_element_value_copy(xml_obj, PCMK_XA_HOST_NETMASK);
    1076           0 :         bundle_data->host_network =
    1077           0 :             crm_element_value_copy(xml_obj, PCMK_XA_HOST_INTERFACE);
    1078           0 :         bundle_data->control_port =
    1079           0 :             crm_element_value_copy(xml_obj, PCMK_XA_CONTROL_PORT);
    1080           0 :         value = crm_element_value(xml_obj, PCMK_XA_ADD_HOST);
    1081           0 :         if (crm_str_to_boolean(value, &bundle_data->add_host) != 1) {
    1082           0 :             bundle_data->add_host = TRUE;
    1083             :         }
    1084             : 
    1085           0 :         for (xml_child = pcmk__xe_first_child(xml_obj, PCMK_XE_PORT_MAPPING,
    1086             :                                               NULL, NULL);
    1087           0 :              xml_child != NULL; xml_child = pcmk__xe_next_same(xml_child)) {
    1088             : 
    1089             :             pe__bundle_port_t *port =
    1090           0 :                 pcmk__assert_alloc(1, sizeof(pe__bundle_port_t));
    1091             : 
    1092           0 :             port->source = crm_element_value_copy(xml_child, PCMK_XA_PORT);
    1093             : 
    1094           0 :             if(port->source == NULL) {
    1095           0 :                 port->source = crm_element_value_copy(xml_child, PCMK_XA_RANGE);
    1096             :             } else {
    1097           0 :                 port->target = crm_element_value_copy(xml_child,
    1098             :                                                       PCMK_XA_INTERNAL_PORT);
    1099             :             }
    1100             : 
    1101           0 :             if(port->source != NULL && strlen(port->source) > 0) {
    1102           0 :                 if(port->target == NULL) {
    1103           0 :                     port->target = strdup(port->source);
    1104             :                 }
    1105           0 :                 bundle_data->ports = g_list_append(bundle_data->ports, port);
    1106             : 
    1107             :             } else {
    1108           0 :                 pcmk__config_err("Invalid " PCMK_XA_PORT " directive %s",
    1109             :                                  pcmk__xe_id(xml_child));
    1110           0 :                 port_free(port);
    1111             :             }
    1112             :         }
    1113             :     }
    1114             : 
    1115           0 :     xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_STORAGE, NULL, NULL);
    1116           0 :     for (xml_child = pcmk__xe_first_child(xml_obj, PCMK_XE_STORAGE_MAPPING,
    1117             :                                           NULL, NULL);
    1118           0 :          xml_child != NULL; xml_child = pcmk__xe_next_same(xml_child)) {
    1119             : 
    1120           0 :         const char *source = crm_element_value(xml_child, PCMK_XA_SOURCE_DIR);
    1121           0 :         const char *target = crm_element_value(xml_child, PCMK_XA_TARGET_DIR);
    1122           0 :         const char *options = crm_element_value(xml_child, PCMK_XA_OPTIONS);
    1123           0 :         int flags = pe__bundle_mount_none;
    1124             : 
    1125           0 :         if (source == NULL) {
    1126           0 :             source = crm_element_value(xml_child, PCMK_XA_SOURCE_DIR_ROOT);
    1127           0 :             pe__set_bundle_mount_flags(xml_child, flags,
    1128             :                                        pe__bundle_mount_subdir);
    1129             :         }
    1130             : 
    1131           0 :         if (source && target) {
    1132           0 :             mount_add(bundle_data, source, target, options, flags);
    1133           0 :             if (strcmp(target, "/var/log") == 0) {
    1134           0 :                 need_log_mount = FALSE;
    1135             :             }
    1136             :         } else {
    1137           0 :             pcmk__config_err("Invalid mount directive %s",
    1138             :                              pcmk__xe_id(xml_child));
    1139             :         }
    1140             :     }
    1141             : 
    1142           0 :     xml_obj = pcmk__xe_first_child(rsc->xml, PCMK_XE_PRIMITIVE, NULL, NULL);
    1143           0 :     if (xml_obj && valid_network(bundle_data)) {
    1144           0 :         char *value = NULL;
    1145           0 :         xmlNode *xml_set = NULL;
    1146             : 
    1147           0 :         xml_resource = pcmk__xe_create(NULL, PCMK_XE_CLONE);
    1148             : 
    1149             :         /* @COMPAT We no longer use the <master> tag, but we need to keep it as
    1150             :          * part of the resource name, so that bundles don't restart in a rolling
    1151             :          * upgrade. (It also avoids needing to change regression tests.)
    1152             :          */
    1153           0 :         crm_xml_set_id(xml_resource, "%s-%s", bundle_data->prefix,
    1154           0 :                       (bundle_data->promoted_max? "master"
    1155             :                       : (const char *)xml_resource->name));
    1156             : 
    1157           0 :         xml_set = pcmk__xe_create(xml_resource, PCMK_XE_META_ATTRIBUTES);
    1158           0 :         crm_xml_set_id(xml_set, "%s-%s-meta", bundle_data->prefix, xml_resource->name);
    1159             : 
    1160           0 :         crm_create_nvpair_xml(xml_set, NULL,
    1161             :                               PCMK_META_ORDERED, PCMK_VALUE_TRUE);
    1162             : 
    1163           0 :         value = pcmk__itoa(bundle_data->nreplicas);
    1164           0 :         crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_MAX, value);
    1165           0 :         free(value);
    1166             : 
    1167           0 :         value = pcmk__itoa(bundle_data->nreplicas_per_host);
    1168           0 :         crm_create_nvpair_xml(xml_set, NULL, PCMK_META_CLONE_NODE_MAX, value);
    1169           0 :         free(value);
    1170             : 
    1171           0 :         crm_create_nvpair_xml(xml_set, NULL, PCMK_META_GLOBALLY_UNIQUE,
    1172           0 :                               pcmk__btoa(bundle_data->nreplicas_per_host > 1));
    1173             : 
    1174           0 :         if (bundle_data->promoted_max) {
    1175           0 :             crm_create_nvpair_xml(xml_set, NULL,
    1176             :                                   PCMK_META_PROMOTABLE, PCMK_VALUE_TRUE);
    1177             : 
    1178           0 :             value = pcmk__itoa(bundle_data->promoted_max);
    1179           0 :             crm_create_nvpair_xml(xml_set, NULL, PCMK_META_PROMOTED_MAX, value);
    1180           0 :             free(value);
    1181             :         }
    1182             : 
    1183             :         //crm_xml_add(xml_obj, PCMK_XA_ID, bundle_data->prefix);
    1184           0 :         pcmk__xml_copy(xml_resource, xml_obj);
    1185             : 
    1186           0 :     } else if(xml_obj) {
    1187           0 :         pcmk__config_err("Cannot control %s inside %s without either "
    1188             :                          PCMK_XA_IP_RANGE_START " or " PCMK_XA_CONTROL_PORT,
    1189             :                          rsc->id, pcmk__xe_id(xml_obj));
    1190           0 :         return FALSE;
    1191             :     }
    1192             : 
    1193           0 :     if(xml_resource) {
    1194           0 :         int lpc = 0;
    1195           0 :         GList *childIter = NULL;
    1196           0 :         pe__bundle_port_t *port = NULL;
    1197           0 :         GString *buffer = NULL;
    1198             : 
    1199           0 :         if (pe__unpack_resource(xml_resource, &(bundle_data->child), rsc,
    1200             :                                 scheduler) != pcmk_rc_ok) {
    1201           0 :             return FALSE;
    1202             :         }
    1203             : 
    1204             :         /* Currently, we always map the default authentication key location
    1205             :          * into the same location inside the container.
    1206             :          *
    1207             :          * Ideally, we would respect the host's PCMK_authkey_location, but:
    1208             :          * - it may be different on different nodes;
    1209             :          * - the actual connection will do extra checking to make sure the key
    1210             :          *   file exists and is readable, that we can't do here on the DC
    1211             :          * - tools such as crm_resource and crm_simulate may not have the same
    1212             :          *   environment variables as the cluster, causing operation digests to
    1213             :          *   differ
    1214             :          *
    1215             :          * Always using the default location inside the container is fine,
    1216             :          * because we control the pacemaker_remote environment, and it avoids
    1217             :          * having to pass another environment variable to the container.
    1218             :          *
    1219             :          * @TODO A better solution may be to have only pacemaker_remote use the
    1220             :          * environment variable, and have the cluster nodes use a new
    1221             :          * cluster option for key location. This would introduce the limitation
    1222             :          * of the location being the same on all cluster nodes, but that's
    1223             :          * reasonable.
    1224             :          */
    1225           0 :         mount_add(bundle_data, DEFAULT_REMOTE_KEY_LOCATION,
    1226             :                   DEFAULT_REMOTE_KEY_LOCATION, NULL, pe__bundle_mount_none);
    1227             : 
    1228           0 :         if (need_log_mount) {
    1229           0 :             mount_add(bundle_data, CRM_BUNDLE_DIR, "/var/log", NULL,
    1230             :                       pe__bundle_mount_subdir);
    1231             :         }
    1232             : 
    1233           0 :         port = pcmk__assert_alloc(1, sizeof(pe__bundle_port_t));
    1234           0 :         if(bundle_data->control_port) {
    1235           0 :             port->source = strdup(bundle_data->control_port);
    1236             :         } else {
    1237             :             /* If we wanted to respect PCMK_remote_port, we could use
    1238             :              * crm_default_remote_port() here and elsewhere in this file instead
    1239             :              * of DEFAULT_REMOTE_PORT.
    1240             :              *
    1241             :              * However, it gains nothing, since we control both the container
    1242             :              * environment and the connection resource parameters, and the user
    1243             :              * can use a different port if desired by setting
    1244             :              * PCMK_XA_CONTROL_PORT.
    1245             :              */
    1246           0 :             port->source = pcmk__itoa(DEFAULT_REMOTE_PORT);
    1247             :         }
    1248           0 :         port->target = strdup(port->source);
    1249           0 :         bundle_data->ports = g_list_append(bundle_data->ports, port);
    1250             : 
    1251           0 :         buffer = g_string_sized_new(1024);
    1252           0 :         for (childIter = bundle_data->child->children; childIter != NULL;
    1253           0 :              childIter = childIter->next) {
    1254             : 
    1255           0 :             pcmk__bundle_replica_t *replica = NULL;
    1256             : 
    1257           0 :             replica = pcmk__assert_alloc(1, sizeof(pcmk__bundle_replica_t));
    1258           0 :             replica->child = childIter->data;
    1259           0 :             replica->child->exclusive_discover = TRUE;
    1260           0 :             replica->offset = lpc++;
    1261             : 
    1262             :             // Ensure the child's notify gets set based on the underlying primitive's value
    1263           0 :             if (pcmk_is_set(replica->child->flags, pcmk_rsc_notify)) {
    1264           0 :                 pcmk__set_rsc_flags(bundle_data->child, pcmk_rsc_notify);
    1265             :             }
    1266             : 
    1267           0 :             allocate_ip(bundle_data, replica, buffer);
    1268           0 :             bundle_data->replicas = g_list_append(bundle_data->replicas,
    1269             :                                                   replica);
    1270           0 :             bundle_data->attribute_target =
    1271           0 :                 g_hash_table_lookup(replica->child->meta,
    1272             :                                     PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
    1273             :         }
    1274           0 :         bundle_data->container_host_options = g_string_free(buffer, FALSE);
    1275             : 
    1276           0 :         if (bundle_data->attribute_target) {
    1277           0 :             pcmk__insert_dup(rsc->meta, PCMK_META_CONTAINER_ATTRIBUTE_TARGET,
    1278             :                              bundle_data->attribute_target);
    1279           0 :             pcmk__insert_dup(bundle_data->child->meta,
    1280             :                              PCMK_META_CONTAINER_ATTRIBUTE_TARGET,
    1281             :                              bundle_data->attribute_target);
    1282             :         }
    1283             : 
    1284             :     } else {
    1285             :         // Just a naked container, no pacemaker-remote
    1286           0 :         GString *buffer = g_string_sized_new(1024);
    1287             : 
    1288           0 :         for (int lpc = 0; lpc < bundle_data->nreplicas; lpc++) {
    1289           0 :             pcmk__bundle_replica_t *replica = NULL;
    1290             : 
    1291           0 :             replica = pcmk__assert_alloc(1, sizeof(pcmk__bundle_replica_t));
    1292           0 :             replica->offset = lpc;
    1293           0 :             allocate_ip(bundle_data, replica, buffer);
    1294           0 :             bundle_data->replicas = g_list_append(bundle_data->replicas,
    1295             :                                                   replica);
    1296             :         }
    1297           0 :         bundle_data->container_host_options = g_string_free(buffer, FALSE);
    1298             :     }
    1299             : 
    1300           0 :     for (GList *gIter = bundle_data->replicas; gIter != NULL;
    1301           0 :          gIter = gIter->next) {
    1302           0 :         pcmk__bundle_replica_t *replica = gIter->data;
    1303             : 
    1304           0 :         if (create_replica_resources(rsc, bundle_data, replica) != pcmk_rc_ok) {
    1305           0 :             pcmk__config_err("Failed unpacking resource %s", rsc->id);
    1306           0 :             rsc->fns->free(rsc);
    1307           0 :             return FALSE;
    1308             :         }
    1309             : 
    1310             :         /* Utilization needs special handling for bundles. It makes no sense for
    1311             :          * the inner primitive to have utilization, because it is tied
    1312             :          * one-to-one to the guest node created by the container resource -- and
    1313             :          * there's no way to set capacities for that guest node anyway.
    1314             :          *
    1315             :          * What the user really wants is to configure utilization for the
    1316             :          * container. However, the schema only allows utilization for
    1317             :          * primitives, and the container resource is implicit anyway, so the
    1318             :          * user can *only* configure utilization for the inner primitive. If
    1319             :          * they do, move the primitive's utilization values to the container.
    1320             :          *
    1321             :          * @TODO This means that bundles without an inner primitive can't have
    1322             :          * utilization. An alternative might be to allow utilization values in
    1323             :          * the top-level bundle XML in the schema, and copy those to each
    1324             :          * container.
    1325             :          */
    1326           0 :         if (replica->child != NULL) {
    1327           0 :             GHashTable *empty = replica->container->utilization;
    1328             : 
    1329           0 :             replica->container->utilization = replica->child->utilization;
    1330           0 :             replica->child->utilization = empty;
    1331             :         }
    1332             :     }
    1333             : 
    1334           0 :     if (bundle_data->child) {
    1335           0 :         rsc->children = g_list_append(rsc->children, bundle_data->child);
    1336             :     }
    1337           0 :     return TRUE;
    1338             : }
    1339             : 
    1340             : static int
    1341           0 : replica_resource_active(pcmk_resource_t *rsc, gboolean all)
    1342             : {
    1343           0 :     if (rsc) {
    1344           0 :         gboolean child_active = rsc->fns->active(rsc, all);
    1345             : 
    1346           0 :         if (child_active && !all) {
    1347           0 :             return TRUE;
    1348           0 :         } else if (!child_active && all) {
    1349           0 :             return FALSE;
    1350             :         }
    1351             :     }
    1352           0 :     return -1;
    1353             : }
    1354             : 
    1355             : gboolean
    1356           0 : pe__bundle_active(pcmk_resource_t *rsc, gboolean all)
    1357             : {
    1358           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    1359           0 :     GList *iter = NULL;
    1360             : 
    1361           0 :     get_bundle_variant_data(bundle_data, rsc);
    1362           0 :     for (iter = bundle_data->replicas; iter != NULL; iter = iter->next) {
    1363           0 :         pcmk__bundle_replica_t *replica = iter->data;
    1364             :         int rsc_active;
    1365             : 
    1366           0 :         rsc_active = replica_resource_active(replica->ip, all);
    1367           0 :         if (rsc_active >= 0) {
    1368           0 :             return (gboolean) rsc_active;
    1369             :         }
    1370             : 
    1371           0 :         rsc_active = replica_resource_active(replica->child, all);
    1372           0 :         if (rsc_active >= 0) {
    1373           0 :             return (gboolean) rsc_active;
    1374             :         }
    1375             : 
    1376           0 :         rsc_active = replica_resource_active(replica->container, all);
    1377           0 :         if (rsc_active >= 0) {
    1378           0 :             return (gboolean) rsc_active;
    1379             :         }
    1380             : 
    1381           0 :         rsc_active = replica_resource_active(replica->remote, all);
    1382           0 :         if (rsc_active >= 0) {
    1383           0 :             return (gboolean) rsc_active;
    1384             :         }
    1385             :     }
    1386             : 
    1387             :     /* If "all" is TRUE, we've already checked that no resources were inactive,
    1388             :      * so return TRUE; if "all" is FALSE, we didn't find any active resources,
    1389             :      * so return FALSE.
    1390             :      */
    1391           0 :     return all;
    1392             : }
    1393             : 
    1394             : /*!
    1395             :  * \internal
    1396             :  * \brief Find the bundle replica corresponding to a given node
    1397             :  *
    1398             :  * \param[in] bundle  Top-level bundle resource
    1399             :  * \param[in] node    Node to search for
    1400             :  *
    1401             :  * \return Bundle replica if found, NULL otherwise
    1402             :  */
    1403             : pcmk_resource_t *
    1404           0 : pe__find_bundle_replica(const pcmk_resource_t *bundle, const pcmk_node_t *node)
    1405             : {
    1406           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    1407           0 :     CRM_ASSERT(bundle && node);
    1408             : 
    1409           0 :     get_bundle_variant_data(bundle_data, bundle);
    1410           0 :     for (GList *gIter = bundle_data->replicas; gIter != NULL;
    1411           0 :          gIter = gIter->next) {
    1412           0 :         pcmk__bundle_replica_t *replica = gIter->data;
    1413             : 
    1414           0 :         CRM_ASSERT(replica && replica->node);
    1415           0 :         if (pcmk__same_node(replica->node, node)) {
    1416           0 :             return replica->child;
    1417             :         }
    1418             :     }
    1419           0 :     return NULL;
    1420             : }
    1421             : 
    1422             : /*!
    1423             :  * \internal
    1424             :  * \deprecated This function will be removed in a future release
    1425             :  */
    1426             : static void
    1427           0 : print_rsc_in_list(pcmk_resource_t *rsc, const char *pre_text, long options,
    1428             :                   void *print_data)
    1429             : {
    1430           0 :     if (rsc != NULL) {
    1431           0 :         if (options & pe_print_html) {
    1432           0 :             status_print("<li>");
    1433             :         }
    1434           0 :         rsc->fns->print(rsc, pre_text, options, print_data);
    1435           0 :         if (options & pe_print_html) {
    1436           0 :             status_print("</li>\n");
    1437             :         }
    1438             :     }
    1439           0 : }
    1440             : 
    1441             : /*!
    1442             :  * \internal
    1443             :  * \deprecated This function will be removed in a future release
    1444             :  */
    1445             : static void
    1446           0 : bundle_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
    1447             :                  void *print_data)
    1448             : {
    1449           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    1450           0 :     char *child_text = NULL;
    1451           0 :     CRM_CHECK(rsc != NULL, return);
    1452             : 
    1453           0 :     if (pre_text == NULL) {
    1454           0 :         pre_text = "";
    1455             :     }
    1456           0 :     child_text = crm_strdup_printf("%s        ", pre_text);
    1457             : 
    1458           0 :     get_bundle_variant_data(bundle_data, rsc);
    1459             : 
    1460           0 :     status_print("%s<bundle ", pre_text);
    1461           0 :     status_print(PCMK_XA_ID "=\"%s\" ", rsc->id);
    1462           0 :     status_print("type=\"%s\" ", container_agent_str(bundle_data->agent_type));
    1463           0 :     status_print("image=\"%s\" ", bundle_data->image);
    1464           0 :     status_print("unique=\"%s\" ",
    1465             :                  pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
    1466           0 :     status_print("managed=\"%s\" ",
    1467             :                  pcmk__flag_text(rsc->flags, pcmk_rsc_managed));
    1468           0 :     status_print("failed=\"%s\" ",
    1469             :                  pcmk__flag_text(rsc->flags, pcmk_rsc_failed));
    1470           0 :     status_print(">\n");
    1471             : 
    1472           0 :     for (GList *gIter = bundle_data->replicas; gIter != NULL;
    1473           0 :          gIter = gIter->next) {
    1474           0 :         pcmk__bundle_replica_t *replica = gIter->data;
    1475             : 
    1476           0 :         CRM_ASSERT(replica);
    1477           0 :         status_print("%s    <replica " PCMK_XA_ID "=\"%d\">\n",
    1478             :                      pre_text, replica->offset);
    1479           0 :         print_rsc_in_list(replica->ip, child_text, options, print_data);
    1480           0 :         print_rsc_in_list(replica->child, child_text, options, print_data);
    1481           0 :         print_rsc_in_list(replica->container, child_text, options, print_data);
    1482           0 :         print_rsc_in_list(replica->remote, child_text, options, print_data);
    1483           0 :         status_print("%s    </replica>\n", pre_text);
    1484             :     }
    1485           0 :     status_print("%s</bundle>\n", pre_text);
    1486           0 :     free(child_text);
    1487             : }
    1488             : 
    1489             : PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
    1490             :                   "GList *")
    1491             : int
    1492           0 : pe__bundle_xml(pcmk__output_t *out, va_list args)
    1493             : {
    1494           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    1495           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
    1496           0 :     GList *only_node = va_arg(args, GList *);
    1497           0 :     GList *only_rsc = va_arg(args, GList *);
    1498             : 
    1499           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    1500           0 :     int rc = pcmk_rc_no_output;
    1501           0 :     gboolean printed_header = FALSE;
    1502           0 :     gboolean print_everything = TRUE;
    1503             : 
    1504           0 :     const char *desc = NULL;
    1505             : 
    1506           0 :     CRM_ASSERT(rsc != NULL);
    1507             :     
    1508           0 :     get_bundle_variant_data(bundle_data, rsc);
    1509             : 
    1510           0 :     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
    1511           0 :         return rc;
    1512             :     }
    1513             : 
    1514           0 :     print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
    1515             : 
    1516           0 :     for (GList *gIter = bundle_data->replicas; gIter != NULL;
    1517           0 :          gIter = gIter->next) {
    1518           0 :         pcmk__bundle_replica_t *replica = gIter->data;
    1519           0 :         char *id = NULL;
    1520             :         gboolean print_ip, print_child, print_ctnr, print_remote;
    1521             : 
    1522           0 :         CRM_ASSERT(replica);
    1523             : 
    1524           0 :         if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
    1525           0 :             continue;
    1526             :         }
    1527             : 
    1528           0 :         print_ip = replica->ip != NULL &&
    1529           0 :                    !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
    1530           0 :         print_child = replica->child != NULL &&
    1531           0 :                       !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
    1532           0 :         print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
    1533           0 :         print_remote = replica->remote != NULL &&
    1534           0 :                        !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
    1535             : 
    1536           0 :         if (!print_everything && !print_ip && !print_child && !print_ctnr && !print_remote) {
    1537           0 :             continue;
    1538             :         }
    1539             : 
    1540           0 :         if (!printed_header) {
    1541           0 :             const char *type = container_agent_str(bundle_data->agent_type);
    1542           0 :             const char *unique = pcmk__flag_text(rsc->flags, pcmk_rsc_unique);
    1543           0 :             const char *maintenance = pcmk__flag_text(rsc->flags,
    1544             :                                                       pcmk_rsc_maintenance);
    1545           0 :             const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed);
    1546           0 :             const char *failed = pcmk__flag_text(rsc->flags, pcmk_rsc_failed);
    1547             : 
    1548           0 :             printed_header = TRUE;
    1549             : 
    1550           0 :             desc = pe__resource_description(rsc, show_opts);
    1551             : 
    1552           0 :             rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_BUNDLE,
    1553             :                                           PCMK_XA_ID, rsc->id,
    1554             :                                           PCMK_XA_TYPE, type,
    1555             :                                           PCMK_XA_IMAGE, bundle_data->image,
    1556             :                                           PCMK_XA_UNIQUE, unique,
    1557             :                                           PCMK_XA_MAINTENANCE, maintenance,
    1558             :                                           PCMK_XA_MANAGED, managed,
    1559             :                                           PCMK_XA_FAILED, failed,
    1560             :                                           PCMK_XA_DESCRIPTION, desc,
    1561             :                                           NULL);
    1562           0 :             CRM_ASSERT(rc == pcmk_rc_ok);
    1563             :         }
    1564             : 
    1565           0 :         id = pcmk__itoa(replica->offset);
    1566           0 :         rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_REPLICA,
    1567             :                                       PCMK_XA_ID, id,
    1568             :                                       NULL);
    1569           0 :         free(id);
    1570           0 :         CRM_ASSERT(rc == pcmk_rc_ok);
    1571             : 
    1572           0 :         if (print_ip) {
    1573           0 :             out->message(out, (const char *) replica->ip->xml->name, show_opts,
    1574             :                          replica->ip, only_node, only_rsc);
    1575             :         }
    1576             : 
    1577           0 :         if (print_child) {
    1578           0 :             out->message(out, (const char *) replica->child->xml->name,
    1579             :                          show_opts, replica->child, only_node, only_rsc);
    1580             :         }
    1581             : 
    1582           0 :         if (print_ctnr) {
    1583           0 :             out->message(out, (const char *) replica->container->xml->name,
    1584             :                          show_opts, replica->container, only_node, only_rsc);
    1585             :         }
    1586             : 
    1587           0 :         if (print_remote) {
    1588           0 :             out->message(out, (const char *) replica->remote->xml->name,
    1589             :                          show_opts, replica->remote, only_node, only_rsc);
    1590             :         }
    1591             : 
    1592           0 :         pcmk__output_xml_pop_parent(out); // replica
    1593             :     }
    1594             : 
    1595           0 :     if (printed_header) {
    1596           0 :         pcmk__output_xml_pop_parent(out); // bundle
    1597             :     }
    1598             : 
    1599           0 :     return rc;
    1600             : }
    1601             : 
    1602             : static void
    1603           0 : pe__bundle_replica_output_html(pcmk__output_t *out,
    1604             :                                pcmk__bundle_replica_t *replica,
    1605             :                                pcmk_node_t *node, uint32_t show_opts)
    1606             : {
    1607           0 :     pcmk_resource_t *rsc = replica->child;
    1608             : 
    1609           0 :     int offset = 0;
    1610             :     char buffer[LINE_MAX];
    1611             : 
    1612           0 :     if(rsc == NULL) {
    1613           0 :         rsc = replica->container;
    1614             :     }
    1615             : 
    1616           0 :     if (replica->remote) {
    1617           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
    1618           0 :                            rsc_printable_id(replica->remote));
    1619             :     } else {
    1620           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
    1621           0 :                            rsc_printable_id(replica->container));
    1622             :     }
    1623           0 :     if (replica->ipaddr) {
    1624           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
    1625             :                            replica->ipaddr);
    1626             :     }
    1627             : 
    1628           0 :     pe__common_output_html(out, rsc, buffer, node, show_opts);
    1629           0 : }
    1630             : 
    1631             : /*!
    1632             :  * \internal
    1633             :  * \brief Get a string describing a resource's unmanaged state or lack thereof
    1634             :  *
    1635             :  * \param[in] rsc  Resource to describe
    1636             :  *
    1637             :  * \return A string indicating that a resource is in maintenance mode or
    1638             :  *         otherwise unmanaged, or an empty string otherwise
    1639             :  */
    1640             : static const char *
    1641           0 : get_unmanaged_str(const pcmk_resource_t *rsc)
    1642             : {
    1643           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
    1644           0 :         return " (maintenance)";
    1645             :     }
    1646           0 :     if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
    1647           0 :         return " (unmanaged)";
    1648             :     }
    1649           0 :     return "";
    1650             : }
    1651             : 
    1652             : PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
    1653             :                   "GList *")
    1654             : int
    1655           0 : pe__bundle_html(pcmk__output_t *out, va_list args)
    1656             : {
    1657           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    1658           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
    1659           0 :     GList *only_node = va_arg(args, GList *);
    1660           0 :     GList *only_rsc = va_arg(args, GList *);
    1661             : 
    1662           0 :     const char *desc = NULL;
    1663           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    1664           0 :     int rc = pcmk_rc_no_output;
    1665           0 :     gboolean print_everything = TRUE;
    1666             : 
    1667           0 :     CRM_ASSERT(rsc != NULL);
    1668             : 
    1669           0 :     get_bundle_variant_data(bundle_data, rsc);
    1670             : 
    1671           0 :     desc = pe__resource_description(rsc, show_opts);
    1672             : 
    1673           0 :     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
    1674           0 :         return rc;
    1675             :     }
    1676             : 
    1677           0 :     print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
    1678             : 
    1679           0 :     for (GList *gIter = bundle_data->replicas; gIter != NULL;
    1680           0 :          gIter = gIter->next) {
    1681           0 :         pcmk__bundle_replica_t *replica = gIter->data;
    1682             :         gboolean print_ip, print_child, print_ctnr, print_remote;
    1683             : 
    1684           0 :         CRM_ASSERT(replica);
    1685             : 
    1686           0 :         if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
    1687           0 :             continue;
    1688             :         }
    1689             : 
    1690           0 :         print_ip = replica->ip != NULL &&
    1691           0 :                    !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
    1692           0 :         print_child = replica->child != NULL &&
    1693           0 :                       !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
    1694           0 :         print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
    1695           0 :         print_remote = replica->remote != NULL &&
    1696           0 :                        !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
    1697             : 
    1698           0 :         if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
    1699           0 :             (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
    1700             :             /* The text output messages used below require pe_print_implicit to
    1701             :              * be set to do anything.
    1702             :              */
    1703           0 :             uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
    1704             : 
    1705           0 :             PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
    1706             :                                      (bundle_data->nreplicas > 1)? " set" : "",
    1707             :                                      rsc->id, bundle_data->image,
    1708             :                                      pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
    1709             :                                      desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
    1710             :                                      get_unmanaged_str(rsc));
    1711             : 
    1712           0 :             if (pcmk__list_of_multiple(bundle_data->replicas)) {
    1713           0 :                 out->begin_list(out, NULL, NULL, "Replica[%d]", replica->offset);
    1714             :             }
    1715             : 
    1716           0 :             if (print_ip) {
    1717           0 :                 out->message(out, (const char *) replica->ip->xml->name,
    1718             :                              new_show_opts, replica->ip, only_node, only_rsc);
    1719             :             }
    1720             : 
    1721           0 :             if (print_child) {
    1722           0 :                 out->message(out, (const char *) replica->child->xml->name,
    1723             :                              new_show_opts, replica->child, only_node,
    1724             :                              only_rsc);
    1725             :             }
    1726             : 
    1727           0 :             if (print_ctnr) {
    1728           0 :                 out->message(out, (const char *) replica->container->xml->name,
    1729             :                              new_show_opts, replica->container, only_node,
    1730             :                              only_rsc);
    1731             :             }
    1732             : 
    1733           0 :             if (print_remote) {
    1734           0 :                 out->message(out, (const char *) replica->remote->xml->name,
    1735             :                              new_show_opts, replica->remote, only_node,
    1736             :                              only_rsc);
    1737             :             }
    1738             : 
    1739           0 :             if (pcmk__list_of_multiple(bundle_data->replicas)) {
    1740           0 :                 out->end_list(out);
    1741             :             }
    1742           0 :         } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
    1743           0 :             continue;
    1744             :         } else {
    1745           0 :             PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
    1746             :                                      (bundle_data->nreplicas > 1)? " set" : "",
    1747             :                                      rsc->id, bundle_data->image,
    1748             :                                      pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
    1749             :                                      desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
    1750             :                                      get_unmanaged_str(rsc));
    1751             : 
    1752           0 :             pe__bundle_replica_output_html(out, replica,
    1753           0 :                                            pcmk__current_node(replica->container),
    1754             :                                            show_opts);
    1755             :         }
    1756             :     }
    1757             : 
    1758           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    1759           0 :     return rc;
    1760             : }
    1761             : 
    1762             : static void
    1763           0 : pe__bundle_replica_output_text(pcmk__output_t *out,
    1764             :                                pcmk__bundle_replica_t *replica,
    1765             :                                pcmk_node_t *node, uint32_t show_opts)
    1766             : {
    1767           0 :     const pcmk_resource_t *rsc = replica->child;
    1768             : 
    1769           0 :     int offset = 0;
    1770             :     char buffer[LINE_MAX];
    1771             : 
    1772           0 :     if(rsc == NULL) {
    1773           0 :         rsc = replica->container;
    1774             :     }
    1775             : 
    1776           0 :     if (replica->remote) {
    1777           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
    1778           0 :                            rsc_printable_id(replica->remote));
    1779             :     } else {
    1780           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
    1781           0 :                            rsc_printable_id(replica->container));
    1782             :     }
    1783           0 :     if (replica->ipaddr) {
    1784           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
    1785             :                            replica->ipaddr);
    1786             :     }
    1787             : 
    1788           0 :     pe__common_output_text(out, rsc, buffer, node, show_opts);
    1789           0 : }
    1790             : 
    1791             : PCMK__OUTPUT_ARGS("bundle", "uint32_t", "pcmk_resource_t *", "GList *",
    1792             :                   "GList *")
    1793             : int
    1794           0 : pe__bundle_text(pcmk__output_t *out, va_list args)
    1795             : {
    1796           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    1797           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
    1798           0 :     GList *only_node = va_arg(args, GList *);
    1799           0 :     GList *only_rsc = va_arg(args, GList *);
    1800             : 
    1801           0 :     const char *desc = NULL;
    1802           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    1803           0 :     int rc = pcmk_rc_no_output;
    1804           0 :     gboolean print_everything = TRUE;
    1805             : 
    1806           0 :     desc = pe__resource_description(rsc, show_opts);
    1807             :     
    1808           0 :     get_bundle_variant_data(bundle_data, rsc);
    1809             : 
    1810           0 :     CRM_ASSERT(rsc != NULL);
    1811             : 
    1812           0 :     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
    1813           0 :         return rc;
    1814             :     }
    1815             : 
    1816           0 :     print_everything = pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches);
    1817             : 
    1818           0 :     for (GList *gIter = bundle_data->replicas; gIter != NULL;
    1819           0 :          gIter = gIter->next) {
    1820           0 :         pcmk__bundle_replica_t *replica = gIter->data;
    1821             :         gboolean print_ip, print_child, print_ctnr, print_remote;
    1822             : 
    1823           0 :         CRM_ASSERT(replica);
    1824             : 
    1825           0 :         if (pcmk__rsc_filtered_by_node(replica->container, only_node)) {
    1826           0 :             continue;
    1827             :         }
    1828             : 
    1829           0 :         print_ip = replica->ip != NULL &&
    1830           0 :                    !replica->ip->fns->is_filtered(replica->ip, only_rsc, print_everything);
    1831           0 :         print_child = replica->child != NULL &&
    1832           0 :                       !replica->child->fns->is_filtered(replica->child, only_rsc, print_everything);
    1833           0 :         print_ctnr = !replica->container->fns->is_filtered(replica->container, only_rsc, print_everything);
    1834           0 :         print_remote = replica->remote != NULL &&
    1835           0 :                        !replica->remote->fns->is_filtered(replica->remote, only_rsc, print_everything);
    1836             : 
    1837           0 :         if (pcmk_is_set(show_opts, pcmk_show_implicit_rscs) ||
    1838           0 :             (print_everything == FALSE && (print_ip || print_child || print_ctnr || print_remote))) {
    1839             :             /* The text output messages used below require pe_print_implicit to
    1840             :              * be set to do anything.
    1841             :              */
    1842           0 :             uint32_t new_show_opts = show_opts | pcmk_show_implicit_rscs;
    1843             : 
    1844           0 :             PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
    1845             :                                      (bundle_data->nreplicas > 1)? " set" : "",
    1846             :                                      rsc->id, bundle_data->image,
    1847             :                                      pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
    1848             :                                      desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
    1849             :                                      get_unmanaged_str(rsc));
    1850             : 
    1851           0 :             if (pcmk__list_of_multiple(bundle_data->replicas)) {
    1852           0 :                 out->list_item(out, NULL, "Replica[%d]", replica->offset);
    1853             :             }
    1854             : 
    1855           0 :             out->begin_list(out, NULL, NULL, NULL);
    1856             : 
    1857           0 :             if (print_ip) {
    1858           0 :                 out->message(out, (const char *) replica->ip->xml->name,
    1859             :                              new_show_opts, replica->ip, only_node, only_rsc);
    1860             :             }
    1861             : 
    1862           0 :             if (print_child) {
    1863           0 :                 out->message(out, (const char *) replica->child->xml->name,
    1864             :                              new_show_opts, replica->child, only_node,
    1865             :                              only_rsc);
    1866             :             }
    1867             : 
    1868           0 :             if (print_ctnr) {
    1869           0 :                 out->message(out, (const char *) replica->container->xml->name,
    1870             :                              new_show_opts, replica->container, only_node,
    1871             :                              only_rsc);
    1872             :             }
    1873             : 
    1874           0 :             if (print_remote) {
    1875           0 :                 out->message(out, (const char *) replica->remote->xml->name,
    1876             :                              new_show_opts, replica->remote, only_node,
    1877             :                              only_rsc);
    1878             :             }
    1879             : 
    1880           0 :             out->end_list(out);
    1881           0 :         } else if (print_everything == FALSE && !(print_ip || print_child || print_ctnr || print_remote)) {
    1882           0 :             continue;
    1883             :         } else {
    1884           0 :             PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Container bundle%s: %s [%s]%s%s%s%s%s",
    1885             :                                      (bundle_data->nreplicas > 1)? " set" : "",
    1886             :                                      rsc->id, bundle_data->image,
    1887             :                                      pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
    1888             :                                      desc ? " (" : "", desc ? desc : "", desc ? ")" : "",
    1889             :                                      get_unmanaged_str(rsc));
    1890             : 
    1891           0 :             pe__bundle_replica_output_text(out, replica,
    1892           0 :                                            pcmk__current_node(replica->container),
    1893             :                                            show_opts);
    1894             :         }
    1895             :     }
    1896             : 
    1897           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    1898           0 :     return rc;
    1899             : }
    1900             : 
    1901             : /*!
    1902             :  * \internal
    1903             :  * \deprecated This function will be removed in a future release
    1904             :  */
    1905             : static void
    1906           0 : print_bundle_replica(pcmk__bundle_replica_t *replica, const char *pre_text,
    1907             :                      long options, void *print_data)
    1908             : {
    1909           0 :     pcmk_node_t *node = NULL;
    1910           0 :     pcmk_resource_t *rsc = replica->child;
    1911             : 
    1912           0 :     int offset = 0;
    1913             :     char buffer[LINE_MAX];
    1914             : 
    1915           0 :     if(rsc == NULL) {
    1916           0 :         rsc = replica->container;
    1917             :     }
    1918             : 
    1919           0 :     if (replica->remote) {
    1920           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
    1921           0 :                            rsc_printable_id(replica->remote));
    1922             :     } else {
    1923           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s",
    1924           0 :                            rsc_printable_id(replica->container));
    1925             :     }
    1926           0 :     if (replica->ipaddr) {
    1927           0 :         offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)",
    1928             :                            replica->ipaddr);
    1929             :     }
    1930             : 
    1931           0 :     node = pcmk__current_node(replica->container);
    1932           0 :     common_print(rsc, pre_text, buffer, node, options, print_data);
    1933           0 : }
    1934             : 
    1935             : /*!
    1936             :  * \internal
    1937             :  * \deprecated This function will be removed in a future release
    1938             :  */
    1939             : void
    1940           0 : pe__print_bundle(pcmk_resource_t *rsc, const char *pre_text, long options,
    1941             :                  void *print_data)
    1942             : {
    1943           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    1944           0 :     char *child_text = NULL;
    1945           0 :     CRM_CHECK(rsc != NULL, return);
    1946             : 
    1947           0 :     if (options & pe_print_xml) {
    1948           0 :         bundle_print_xml(rsc, pre_text, options, print_data);
    1949           0 :         return;
    1950             :     }
    1951             : 
    1952           0 :     get_bundle_variant_data(bundle_data, rsc);
    1953             : 
    1954           0 :     if (pre_text == NULL) {
    1955           0 :         pre_text = " ";
    1956             :     }
    1957             : 
    1958           0 :     status_print("%sContainer bundle%s: %s [%s]%s%s\n",
    1959             :                  pre_text, ((bundle_data->nreplicas > 1)? " set" : ""),
    1960             :                  rsc->id, bundle_data->image,
    1961             :                  pcmk_is_set(rsc->flags, pcmk_rsc_unique)? " (unique)" : "",
    1962             :                  pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " (unmanaged)");
    1963           0 :     if (options & pe_print_html) {
    1964           0 :         status_print("<br />\n<ul>\n");
    1965             :     }
    1966             : 
    1967             : 
    1968           0 :     for (GList *gIter = bundle_data->replicas; gIter != NULL;
    1969           0 :          gIter = gIter->next) {
    1970           0 :         pcmk__bundle_replica_t *replica = gIter->data;
    1971             : 
    1972           0 :         CRM_ASSERT(replica);
    1973           0 :         if (options & pe_print_html) {
    1974           0 :             status_print("<li>");
    1975             :         }
    1976             : 
    1977           0 :         if (pcmk_is_set(options, pe_print_implicit)) {
    1978           0 :             child_text = crm_strdup_printf("     %s", pre_text);
    1979           0 :             if (pcmk__list_of_multiple(bundle_data->replicas)) {
    1980           0 :                 status_print("  %sReplica[%d]\n", pre_text, replica->offset);
    1981             :             }
    1982           0 :             if (options & pe_print_html) {
    1983           0 :                 status_print("<br />\n<ul>\n");
    1984             :             }
    1985           0 :             print_rsc_in_list(replica->ip, child_text, options, print_data);
    1986           0 :             print_rsc_in_list(replica->container, child_text, options, print_data);
    1987           0 :             print_rsc_in_list(replica->remote, child_text, options, print_data);
    1988           0 :             print_rsc_in_list(replica->child, child_text, options, print_data);
    1989           0 :             if (options & pe_print_html) {
    1990           0 :                 status_print("</ul>\n");
    1991             :             }
    1992             :         } else {
    1993           0 :             child_text = crm_strdup_printf("%s  ", pre_text);
    1994           0 :             print_bundle_replica(replica, child_text, options, print_data);
    1995             :         }
    1996           0 :         free(child_text);
    1997             : 
    1998           0 :         if (options & pe_print_html) {
    1999           0 :             status_print("</li>\n");
    2000             :         }
    2001             :     }
    2002           0 :     if (options & pe_print_html) {
    2003           0 :         status_print("</ul>\n");
    2004             :     }
    2005             : }
    2006             : 
    2007             : static void
    2008           0 : free_bundle_replica(pcmk__bundle_replica_t *replica)
    2009             : {
    2010           0 :     if (replica == NULL) {
    2011           0 :         return;
    2012             :     }
    2013             : 
    2014           0 :     if (replica->node) {
    2015           0 :         free(replica->node);
    2016           0 :         replica->node = NULL;
    2017             :     }
    2018             : 
    2019           0 :     if (replica->ip) {
    2020           0 :         free_xml(replica->ip->xml);
    2021           0 :         replica->ip->xml = NULL;
    2022           0 :         replica->ip->fns->free(replica->ip);
    2023           0 :         replica->ip = NULL;
    2024             :     }
    2025           0 :     if (replica->container) {
    2026           0 :         free_xml(replica->container->xml);
    2027           0 :         replica->container->xml = NULL;
    2028           0 :         replica->container->fns->free(replica->container);
    2029           0 :         replica->container = NULL;
    2030             :     }
    2031           0 :     if (replica->remote) {
    2032           0 :         free_xml(replica->remote->xml);
    2033           0 :         replica->remote->xml = NULL;
    2034           0 :         replica->remote->fns->free(replica->remote);
    2035           0 :         replica->remote = NULL;
    2036             :     }
    2037           0 :     free(replica->ipaddr);
    2038           0 :     free(replica);
    2039             : }
    2040             : 
    2041             : void
    2042           0 : pe__free_bundle(pcmk_resource_t *rsc)
    2043             : {
    2044           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    2045           0 :     CRM_CHECK(rsc != NULL, return);
    2046             : 
    2047           0 :     get_bundle_variant_data(bundle_data, rsc);
    2048           0 :     pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
    2049             : 
    2050           0 :     free(bundle_data->prefix);
    2051           0 :     free(bundle_data->image);
    2052           0 :     free(bundle_data->control_port);
    2053           0 :     free(bundle_data->host_network);
    2054           0 :     free(bundle_data->host_netmask);
    2055           0 :     free(bundle_data->ip_range_start);
    2056           0 :     free(bundle_data->container_network);
    2057           0 :     free(bundle_data->launcher_options);
    2058           0 :     free(bundle_data->container_command);
    2059           0 :     g_free(bundle_data->container_host_options);
    2060             : 
    2061           0 :     g_list_free_full(bundle_data->replicas,
    2062             :                      (GDestroyNotify) free_bundle_replica);
    2063           0 :     g_list_free_full(bundle_data->mounts, (GDestroyNotify)mount_free);
    2064           0 :     g_list_free_full(bundle_data->ports, (GDestroyNotify)port_free);
    2065           0 :     g_list_free(rsc->children);
    2066             : 
    2067           0 :     if(bundle_data->child) {
    2068           0 :         free_xml(bundle_data->child->xml);
    2069           0 :         bundle_data->child->xml = NULL;
    2070           0 :         bundle_data->child->fns->free(bundle_data->child);
    2071             :     }
    2072           0 :     common_free(rsc);
    2073             : }
    2074             : 
    2075             : enum rsc_role_e
    2076           0 : pe__bundle_resource_state(const pcmk_resource_t *rsc, gboolean current)
    2077             : {
    2078           0 :     enum rsc_role_e container_role = pcmk_role_unknown;
    2079           0 :     return container_role;
    2080             : }
    2081             : 
    2082             : /*!
    2083             :  * \brief Get the number of configured replicas in a bundle
    2084             :  *
    2085             :  * \param[in] rsc  Bundle resource
    2086             :  *
    2087             :  * \return Number of configured replicas, or 0 on error
    2088             :  */
    2089             : int
    2090           0 : pe_bundle_replicas(const pcmk_resource_t *rsc)
    2091             : {
    2092           0 :     if ((rsc == NULL) || (rsc->variant != pcmk_rsc_variant_bundle)) {
    2093           0 :         return 0;
    2094             :     } else {
    2095           0 :         pe__bundle_variant_data_t *bundle_data = NULL;
    2096             : 
    2097           0 :         get_bundle_variant_data(bundle_data, rsc);
    2098           0 :         return bundle_data->nreplicas;
    2099             :     }
    2100             : }
    2101             : 
    2102             : void
    2103           0 : pe__count_bundle(pcmk_resource_t *rsc)
    2104             : {
    2105           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    2106             : 
    2107           0 :     get_bundle_variant_data(bundle_data, rsc);
    2108           0 :     for (GList *item = bundle_data->replicas; item != NULL; item = item->next) {
    2109           0 :         pcmk__bundle_replica_t *replica = item->data;
    2110             : 
    2111           0 :         if (replica->ip) {
    2112           0 :             replica->ip->fns->count(replica->ip);
    2113             :         }
    2114           0 :         if (replica->child) {
    2115           0 :             replica->child->fns->count(replica->child);
    2116             :         }
    2117           0 :         if (replica->container) {
    2118           0 :             replica->container->fns->count(replica->container);
    2119             :         }
    2120           0 :         if (replica->remote) {
    2121           0 :             replica->remote->fns->count(replica->remote);
    2122             :         }
    2123             :     }
    2124           0 : }
    2125             : 
    2126             : gboolean
    2127           0 : pe__bundle_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
    2128             :                        gboolean check_parent)
    2129             : {
    2130           0 :     gboolean passes = FALSE;
    2131           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    2132             : 
    2133           0 :     if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
    2134           0 :         passes = TRUE;
    2135             :     } else {
    2136           0 :         get_bundle_variant_data(bundle_data, rsc);
    2137             : 
    2138           0 :         for (GList *gIter = bundle_data->replicas; gIter != NULL; gIter = gIter->next) {
    2139           0 :             pcmk__bundle_replica_t *replica = gIter->data;
    2140             : 
    2141           0 :             if (replica->ip != NULL && !replica->ip->fns->is_filtered(replica->ip, only_rsc, FALSE)) {
    2142           0 :                 passes = TRUE;
    2143           0 :                 break;
    2144           0 :             } else if (replica->child != NULL && !replica->child->fns->is_filtered(replica->child, only_rsc, FALSE)) {
    2145           0 :                 passes = TRUE;
    2146           0 :                 break;
    2147           0 :             } else if (!replica->container->fns->is_filtered(replica->container, only_rsc, FALSE)) {
    2148           0 :                 passes = TRUE;
    2149           0 :                 break;
    2150           0 :             } else if (replica->remote != NULL && !replica->remote->fns->is_filtered(replica->remote, only_rsc, FALSE)) {
    2151           0 :                 passes = TRUE;
    2152           0 :                 break;
    2153             :             }
    2154             :         }
    2155             :     }
    2156             : 
    2157           0 :     return !passes;
    2158             : }
    2159             : 
    2160             : /*!
    2161             :  * \internal
    2162             :  * \brief Get a list of a bundle's containers
    2163             :  *
    2164             :  * \param[in] bundle  Bundle resource
    2165             :  *
    2166             :  * \return Newly created list of \p bundle's containers
    2167             :  * \note It is the caller's responsibility to free the result with
    2168             :  *       g_list_free().
    2169             :  */
    2170             : GList *
    2171           0 : pe__bundle_containers(const pcmk_resource_t *bundle)
    2172             : {
    2173           0 :     GList *containers = NULL;
    2174           0 :     const pe__bundle_variant_data_t *data = NULL;
    2175             : 
    2176           0 :     get_bundle_variant_data(data, bundle);
    2177           0 :     for (GList *iter = data->replicas; iter != NULL; iter = iter->next) {
    2178           0 :         pcmk__bundle_replica_t *replica = iter->data;
    2179             : 
    2180           0 :         containers = g_list_append(containers, replica->container);
    2181             :     }
    2182           0 :     return containers;
    2183             : }
    2184             : 
    2185             : // Bundle implementation of pcmk_rsc_methods_t:active_node()
    2186             : pcmk_node_t *
    2187           0 : pe__bundle_active_node(const pcmk_resource_t *rsc, unsigned int *count_all,
    2188             :                        unsigned int *count_clean)
    2189             : {
    2190           0 :     pcmk_node_t *active = NULL;
    2191           0 :     pcmk_node_t *node = NULL;
    2192           0 :     pcmk_resource_t *container = NULL;
    2193           0 :     GList *containers = NULL;
    2194           0 :     GList *iter = NULL;
    2195           0 :     GHashTable *nodes = NULL;
    2196           0 :     const pe__bundle_variant_data_t *data = NULL;
    2197             : 
    2198           0 :     if (count_all != NULL) {
    2199           0 :         *count_all = 0;
    2200             :     }
    2201           0 :     if (count_clean != NULL) {
    2202           0 :         *count_clean = 0;
    2203             :     }
    2204           0 :     if (rsc == NULL) {
    2205           0 :         return NULL;
    2206             :     }
    2207             : 
    2208             :     /* For the purposes of this method, we only care about where the bundle's
    2209             :      * containers are active, so build a list of active containers.
    2210             :      */
    2211           0 :     get_bundle_variant_data(data, rsc);
    2212           0 :     for (iter = data->replicas; iter != NULL; iter = iter->next) {
    2213           0 :         pcmk__bundle_replica_t *replica = iter->data;
    2214             : 
    2215           0 :         if (replica->container->running_on != NULL) {
    2216           0 :             containers = g_list_append(containers, replica->container);
    2217             :         }
    2218             :     }
    2219           0 :     if (containers == NULL) {
    2220           0 :         return NULL;
    2221             :     }
    2222             : 
    2223             :     /* If the bundle has only a single active container, just use that
    2224             :      * container's method. If live migration is ever supported for bundle
    2225             :      * containers, this will allow us to prefer the migration source when there
    2226             :      * is only one container and it is migrating. For now, this just lets us
    2227             :      * avoid creating the nodes table.
    2228             :      */
    2229           0 :     if (pcmk__list_of_1(containers)) {
    2230           0 :         container = containers->data;
    2231           0 :         node = container->fns->active_node(container, count_all, count_clean);
    2232           0 :         g_list_free(containers);
    2233           0 :         return node;
    2234             :     }
    2235             : 
    2236             :     // Add all containers' active nodes to a hash table (for uniqueness)
    2237           0 :     nodes = g_hash_table_new(NULL, NULL);
    2238           0 :     for (iter = containers; iter != NULL; iter = iter->next) {
    2239           0 :         container = iter->data;
    2240             : 
    2241           0 :         for (GList *node_iter = container->running_on; node_iter != NULL;
    2242           0 :              node_iter = node_iter->next) {
    2243           0 :             node = node_iter->data;
    2244             : 
    2245             :             // If insert returns true, we haven't counted this node yet
    2246           0 :             if (g_hash_table_insert(nodes, (gpointer) node->details,
    2247             :                                     (gpointer) node)
    2248           0 :                 && !pe__count_active_node(rsc, node, &active, count_all,
    2249             :                                           count_clean)) {
    2250           0 :                 goto done;
    2251             :             }
    2252             :         }
    2253             :     }
    2254             : 
    2255           0 : done:
    2256           0 :     g_list_free(containers);
    2257           0 :     g_hash_table_destroy(nodes);
    2258           0 :     return active;
    2259             : }
    2260             : 
    2261             : /*!
    2262             :  * \internal
    2263             :  * \brief Get maximum bundle resource instances per node
    2264             :  *
    2265             :  * \param[in] rsc  Bundle resource to check
    2266             :  *
    2267             :  * \return Maximum number of \p rsc instances that can be active on one node
    2268             :  */
    2269             : unsigned int
    2270           0 : pe__bundle_max_per_node(const pcmk_resource_t *rsc)
    2271             : {
    2272           0 :     pe__bundle_variant_data_t *bundle_data = NULL;
    2273             : 
    2274           0 :     get_bundle_variant_data(bundle_data, rsc);
    2275           0 :     CRM_ASSERT(bundle_data->nreplicas_per_host >= 0);
    2276           0 :     return (unsigned int) bundle_data->nreplicas_per_host;
    2277             : }

Generated by: LCOV version 1.14