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