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