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