pacemaker  1.1.17-b36b869ca8
Scalable High-Availability cluster resource manager
container.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include <crm_internal.h>
20 
21 #include <crm/pengine/rules.h>
22 #include <crm/pengine/status.h>
23 #include <crm/pengine/internal.h>
24 #include <unpack.h>
25 #include <crm/msg_xml.h>
26 
27 #define VARIANT_CONTAINER 1
28 #include "./variant.h"
29 
30 void tuple_free(container_grouping_t *tuple);
31 
32 static char *
33 next_ip(const char *last_ip)
34 {
35  unsigned int oct1 = 0;
36  unsigned int oct2 = 0;
37  unsigned int oct3 = 0;
38  unsigned int oct4 = 0;
39  int rc = sscanf(last_ip, "%u.%u.%u.%u", &oct1, &oct2, &oct3, &oct4);
40 
41  if (rc != 4) {
42  /*@ TODO check for IPv6 */
43  return NULL;
44 
45  } else if (oct3 > 253) {
46  return NULL;
47 
48  } else if (oct4 > 253) {
49  ++oct3;
50  oct4 = 1;
51 
52  } else {
53  ++oct4;
54  }
55 
56  return crm_strdup_printf("%u.%u.%u.%u", oct1, oct2, oct3, oct4);
57 }
58 
59 static int
60 allocate_ip(container_variant_data_t *data, container_grouping_t *tuple, char *buffer, int max)
61 {
62  if(data->ip_range_start == NULL) {
63  return 0;
64 
65  } else if(data->ip_last) {
66  tuple->ipaddr = next_ip(data->ip_last);
67 
68  } else {
69  tuple->ipaddr = strdup(data->ip_range_start);
70  }
71 
72  data->ip_last = tuple->ipaddr;
73 #if 0
74  return snprintf(buffer, max, " --add-host=%s-%d:%s --link %s-docker-%d:%s-link-%d",
75  data->prefix, tuple->offset, tuple->ipaddr,
76  data->prefix, tuple->offset, data->prefix, tuple->offset);
77 #else
78  return snprintf(buffer, max, " --add-host=%s-%d:%s",
79  data->prefix, tuple->offset, tuple->ipaddr);
80 #endif
81 }
82 
83 static xmlNode *
84 create_resource(const char *name, const char *provider, const char *kind)
85 {
86  xmlNode *rsc = create_xml_node(NULL, XML_CIB_TAG_RESOURCE);
87 
88  crm_xml_add(rsc, XML_ATTR_ID, name);
89  crm_xml_add(rsc, XML_AGENT_ATTR_CLASS, "ocf");
90  crm_xml_add(rsc, XML_AGENT_ATTR_PROVIDER, provider);
91  crm_xml_add(rsc, XML_ATTR_TYPE, kind);
92 
93  return rsc;
94 }
95 
96 static void
97 create_nvp(xmlNode *parent, const char *name, const char *value)
98 {
99  xmlNode *xml_nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
100 
101  crm_xml_set_id(xml_nvp, "%s-%s", ID(parent), name);
102  crm_xml_add(xml_nvp, XML_NVPAIR_ATTR_NAME, name);
103  crm_xml_add(xml_nvp, XML_NVPAIR_ATTR_VALUE, value);
104 }
105 
106 static void
107 create_op(xmlNode *parent, const char *prefix, const char *task, const char *interval)
108 {
109  xmlNode *xml_op = create_xml_node(parent, "op");
110 
111  crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval);
112  crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval);
113  crm_xml_add(xml_op, "name", task);
114 }
115 
128 static bool
129 valid_network(container_variant_data_t *data)
130 {
131  if(data->ip_range_start) {
132  return TRUE;
133  }
134  if(data->control_port) {
135  if(data->replicas_per_host > 1) {
136  pe_err("Specifying the 'control-port' for %s requires 'replicas-per-host=1'", data->prefix);
137  data->replicas_per_host = 1;
138  /* @TODO to be sure: clear_bit(rsc->flags, pe_rsc_unique); */
139  }
140  return TRUE;
141  }
142  return FALSE;
143 }
144 
145 static bool
146 create_ip_resource(
147  resource_t *parent, container_variant_data_t *data, container_grouping_t *tuple,
148  pe_working_set_t * data_set)
149 {
150  if(data->ip_range_start) {
151  char *id = NULL;
152  xmlNode *xml_ip = NULL;
153  xmlNode *xml_obj = NULL;
154 
155  id = crm_strdup_printf("%s-ip-%s", data->prefix, tuple->ipaddr);
157  xml_ip = create_resource(id, "heartbeat", "IPaddr2");
158  free(id);
159 
160  xml_obj = create_xml_node(xml_ip, XML_TAG_ATTR_SETS);
161  crm_xml_set_id(xml_obj, "%s-attributes-%d", data->prefix, tuple->offset);
162 
163  create_nvp(xml_obj, "ip", tuple->ipaddr);
164  if(data->host_network) {
165  create_nvp(xml_obj, "nic", data->host_network);
166  }
167 
168  if(data->host_netmask) {
169  create_nvp(xml_obj, "cidr_netmask", data->host_netmask);
170 
171  } else {
172  create_nvp(xml_obj, "cidr_netmask", "32");
173  }
174 
175  xml_obj = create_xml_node(xml_ip, "operations");
176  create_op(xml_obj, ID(xml_ip), "monitor", "60s");
177 
178  // TODO: Other ops? Timeouts and intervals from underlying resource?
179 
180  if (common_unpack(xml_ip, &tuple->ip, parent, data_set) == false) {
181  return FALSE;
182  }
183 
184  parent->children = g_list_append(parent->children, tuple->ip);
185  }
186  return TRUE;
187 }
188 
189 static bool
190 create_docker_resource(
191  resource_t *parent, container_variant_data_t *data, container_grouping_t *tuple,
192  pe_working_set_t * data_set)
193 {
194  int offset = 0, max = 4096;
195  char *buffer = calloc(1, max+1);
196 
197  int doffset = 0, dmax = 1024;
198  char *dbuffer = calloc(1, dmax+1);
199 
200  char *id = NULL;
201  xmlNode *xml_docker = NULL;
202  xmlNode *xml_obj = NULL;
203 
204  id = crm_strdup_printf("%s-docker-%d", data->prefix, tuple->offset);
206  xml_docker = create_resource(id, "heartbeat", "docker");
207  free(id);
208 
209  xml_obj = create_xml_node(xml_docker, XML_TAG_ATTR_SETS);
210  crm_xml_set_id(xml_obj, "%s-attributes-%d", data->prefix, tuple->offset);
211 
212  create_nvp(xml_obj, "image", data->image);
213  create_nvp(xml_obj, "allow_pull", "true");
214  create_nvp(xml_obj, "force_kill", "false");
215  create_nvp(xml_obj, "reuse", "false");
216 
217  offset += snprintf(buffer+offset, max-offset, " --restart=no");
218 
219  /* Set a container hostname only if we have an IP to map it to.
220  * The user can set -h or --uts=host themselves if they want a nicer
221  * name for logs, but this makes applications happy who need their
222  * hostname to match the IP they bind to.
223  */
224  if (data->ip_range_start != NULL) {
225  offset += snprintf(buffer+offset, max-offset, " -h %s-%d",
226  data->prefix, tuple->offset);
227  }
228 
229  if(data->docker_network) {
230 // offset += snprintf(buffer+offset, max-offset, " --link-local-ip=%s", tuple->ipaddr);
231  offset += snprintf(buffer+offset, max-offset, " --net=%s", data->docker_network);
232  }
233 
234  if(data->control_port) {
235  offset += snprintf(buffer+offset, max-offset, " -e PCMK_remote_port=%s", data->control_port);
236  } else {
237  offset += snprintf(buffer+offset, max-offset, " -e PCMK_remote_port=%d", DEFAULT_REMOTE_PORT);
238  }
239 
240  for(GListPtr pIter = data->mounts; pIter != NULL; pIter = pIter->next) {
241  container_mount_t *mount = pIter->data;
242 
243  if(mount->flags) {
244  char *source = crm_strdup_printf(
245  "%s/%s-%d", mount->source, data->prefix, tuple->offset);
246 
247  if(doffset > 0) {
248  doffset += snprintf(dbuffer+doffset, dmax-doffset, ",");
249  }
250  doffset += snprintf(dbuffer+doffset, dmax-doffset, "%s", source);
251  offset += snprintf(buffer+offset, max-offset, " -v %s:%s", source, mount->target);
252  free(source);
253 
254  } else {
255  offset += snprintf(buffer+offset, max-offset, " -v %s:%s", mount->source, mount->target);
256  }
257  if(mount->options) {
258  offset += snprintf(buffer+offset, max-offset, ":%s", mount->options);
259  }
260  }
261 
262  for(GListPtr pIter = data->ports; pIter != NULL; pIter = pIter->next) {
263  container_port_t *port = pIter->data;
264 
265  if(tuple->ipaddr) {
266  offset += snprintf(buffer+offset, max-offset, " -p %s:%s:%s",
267  tuple->ipaddr, port->source, port->target);
268  } else {
269  offset += snprintf(buffer+offset, max-offset, " -p %s:%s", port->source, port->target);
270  }
271  }
272 
273  if(data->docker_run_options) {
274  offset += snprintf(buffer+offset, max-offset, " %s", data->docker_run_options);
275  }
276 
277  if(data->docker_host_options) {
278  offset += snprintf(buffer+offset, max-offset, " %s", data->docker_host_options);
279  }
280 
281  create_nvp(xml_obj, "run_opts", buffer);
282  free(buffer);
283 
284  create_nvp(xml_obj, "mount_points", dbuffer);
285  free(dbuffer);
286 
287  if(tuple->child) {
288  if(data->docker_run_command) {
289  create_nvp(xml_obj, "run_cmd", data->docker_run_command);
290  } else {
291  create_nvp(xml_obj, "run_cmd", SBIN_DIR"/pacemaker_remoted");
292  }
293 
294  /* TODO: Allow users to specify their own?
295  *
296  * We just want to know if the container is alive, we'll
297  * monitor the child independently
298  */
299  create_nvp(xml_obj, "monitor_cmd", "/bin/true");
300  /* } else if(child && data->untrusted) {
301  * Support this use-case?
302  *
303  * The ability to have resources started/stopped by us, but
304  * unable to set attributes, etc.
305  *
306  * Arguably better to control API access this with ACLs like
307  * "normal" remote nodes
308  *
309  * create_nvp(xml_obj, "run_cmd", "/usr/libexec/pacemaker/lrmd");
310  * create_nvp(xml_obj, "monitor_cmd", "/usr/libexec/pacemaker/lrmd_internal_ctl -c poke");
311  */
312  } else {
313  if(data->docker_run_command) {
314  create_nvp(xml_obj, "run_cmd", data->docker_run_command);
315  }
316 
317  /* TODO: Allow users to specify their own?
318  *
319  * We don't know what's in the container, so we just want
320  * to know if it is alive
321  */
322  create_nvp(xml_obj, "monitor_cmd", "/bin/true");
323  }
324 
325 
326  xml_obj = create_xml_node(xml_docker, "operations");
327  create_op(xml_obj, ID(xml_docker), "monitor", "60s");
328 
329  // TODO: Other ops? Timeouts and intervals from underlying resource?
330 
331  if (common_unpack(xml_docker, &tuple->docker, parent, data_set) == FALSE) {
332  return FALSE;
333  }
334  parent->children = g_list_append(parent->children, tuple->docker);
335  return TRUE;
336 }
337 
344 static void
345 disallow_node(resource_t *rsc, const char *uname)
346 {
347  gpointer match = g_hash_table_lookup(rsc->allowed_nodes, uname);
348 
349  if (match) {
350  ((pe_node_t *) match)->weight = -INFINITY;
351  }
352  if (rsc->children) {
353  GListPtr child;
354 
355  for (child = rsc->children; child != NULL; child = child->next) {
356  disallow_node((resource_t *) (child->data), uname);
357  }
358  }
359 }
360 
361 static bool
362 create_remote_resource(
363  resource_t *parent, container_variant_data_t *data, container_grouping_t *tuple,
364  pe_working_set_t * data_set)
365 {
366  if (tuple->child && valid_network(data)) {
367  GHashTableIter gIter;
368  GListPtr rsc_iter = NULL;
369  node_t *node = NULL;
370  xmlNode *xml_obj = NULL;
371  xmlNode *xml_remote = NULL;
372  char *id = crm_strdup_printf("%s-%d", data->prefix, tuple->offset);
373  const char *uname = NULL;
374 
375  if (remote_id_conflict(id, data_set)) {
376  free(id);
377  // The biggest hammer we have
378  id = crm_strdup_printf("pcmk-internal-%s-remote-%d", tuple->child->id, tuple->offset);
379  CRM_ASSERT(remote_id_conflict(id, data_set) == FALSE);
380  }
381 
382  xml_remote = create_resource(id, "pacemaker", "remote");
383 
384  /* Abandon our created ID, and pull the copy from the XML, because we
385  * need something that will get freed during data set cleanup to use as
386  * the node ID and uname.
387  */
388  free(id);
389  id = NULL;
390  uname = ID(xml_remote);
391 
392  xml_obj = create_xml_node(xml_remote, "operations");
393  create_op(xml_obj, uname, "monitor", "60s");
394 
395  xml_obj = create_xml_node(xml_remote, XML_TAG_ATTR_SETS);
396  crm_xml_set_id(xml_obj, "%s-attributes-%d", data->prefix, tuple->offset);
397 
398  if(tuple->ipaddr) {
399  create_nvp(xml_obj, "addr", tuple->ipaddr);
400  } else {
401  // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
402  create_nvp(xml_obj, "addr", "#uname");
403  }
404 
405  if(data->control_port) {
406  create_nvp(xml_obj, "port", data->control_port);
407  } else {
408  char *port_s = crm_itoa(DEFAULT_REMOTE_PORT);
409 
410  create_nvp(xml_obj, "port", port_s);
411  free(port_s);
412  }
413 
414  xml_obj = create_xml_node(xml_remote, XML_TAG_META_SETS);
415  crm_xml_set_id(xml_obj, "%s-meta-%d", data->prefix, tuple->offset);
416 
417  create_nvp(xml_obj, XML_OP_ATTR_ALLOW_MIGRATE, "false");
418 
419  /* This sets tuple->docker as tuple->remote's container, which is
420  * similar to what happens with guest nodes. This is how the PE knows
421  * that the bundle node is fenced by recovering docker, and that
422  * remote should be ordered relative to docker.
423  */
424  create_nvp(xml_obj, XML_RSC_ATTR_CONTAINER, tuple->docker->id);
425 
426  /* Ensure a node has been created for the guest (it may have already
427  * been, if it has a permanent node attribute), and ensure its weight is
428  * -INFINITY so no other resources can run on it.
429  */
430  node = pe_find_node(data_set->nodes, uname);
431  if (node == NULL) {
432  node = pe_create_node(uname, uname, "remote", "-INFINITY",
433  data_set);
434  } else {
435  node->weight = -INFINITY;
436  }
437 
438  /* unpack_remote_nodes() ensures that each remote node and guest node
439  * has a pe_node_t entry. Ideally, it would do the same for bundle nodes.
440  * Unfortunately, a bundle has to be mostly unpacked before it's obvious
441  * what nodes will be needed, so we do it just above.
442  *
443  * Worse, that means that the node may have been utilized while
444  * unpacking other resources, without our weight correction. The most
445  * likely place for this to happen is when common_unpack() calls
446  * resource_location() to set a default score in symmetric clusters.
447  * This adds a node *copy* to each resource's allowed nodes, and these
448  * copies will have the wrong weight.
449  *
450  * As a hacky workaround, fix those copies here.
451  *
452  * @TODO Possible alternative: ensure bundles are unpacked before other
453  * resources, so the weight is correct before any copies are made.
454  */
455  for (rsc_iter = data_set->resources; rsc_iter; rsc_iter = rsc_iter->next) {
456  disallow_node((resource_t *) (rsc_iter->data), uname);
457  }
458 
459  tuple->node = node_copy(node);
460  tuple->node->weight = 500;
461 
462  if (common_unpack(xml_remote, &tuple->remote, parent, data_set) == FALSE) {
463  return FALSE;
464  }
465 
466  g_hash_table_iter_init(&gIter, tuple->remote->allowed_nodes);
467  while (g_hash_table_iter_next(&gIter, NULL, (void **)&node)) {
468  if(is_remote_node(node)) {
469  /* Remote resources can only run on 'normal' cluster node */
470  node->weight = -INFINITY;
471  }
472  }
473 
474  tuple->node->details->remote_rsc = tuple->remote;
475 
476  /* #kind is irrelevant to bundles since it is only used in location
477  * constraint rules, and those don't matter for resources inside
478  * bundles. But just for clarity, a bundle is closer to "container"
479  * (guest node) than the "remote" set by pe_create_node().
480  */
481  g_hash_table_insert(tuple->node->details->attrs,
482  strdup("#kind"), strdup("container"));
483 
484  /* One effect of this is that setup_container() will add
485  * tuple->remote to tuple->docker's fillers, which will make
486  * rsc_contains_remote_node() true for tuple->docker.
487  *
488  * tuple->child does NOT get added to tuple->docker's fillers.
489  * The only noticeable effect if it did would be for its fail count to
490  * be taken into account when checking tuple->docker's migration
491  * threshold.
492  */
493  parent->children = g_list_append(parent->children, tuple->remote);
494  }
495  return TRUE;
496 }
497 
498 static bool
499 create_container(
500  resource_t *parent, container_variant_data_t *data, container_grouping_t *tuple,
501  pe_working_set_t * data_set)
502 {
503 
504  if(create_docker_resource(parent, data, tuple, data_set) == FALSE) {
505  return TRUE;
506  }
507  if(create_ip_resource(parent, data, tuple, data_set) == FALSE) {
508  return TRUE;
509  }
510  if(create_remote_resource(parent, data, tuple, data_set) == FALSE) {
511  return TRUE;
512  }
513  if(tuple->child && tuple->ipaddr) {
514  add_hash_param(tuple->child->meta, "external-ip", tuple->ipaddr);
515  }
516 
517  if(tuple->remote) {
518  /*
519  * Allow the remote connection resource to be allocated to a
520  * different node than the one on which the docker container
521  * is active.
522  *
523  * Makes it possible to have remote nodes, running docker
524  * containers with pacemaker_remoted inside in order to start
525  * services inside those containers.
526  */
527  set_bit(tuple->remote->flags, pe_rsc_allow_remote_remotes);
528  }
529 
530  return FALSE;
531 }
532 
533 static void mount_free(container_mount_t *mount)
534 {
535  free(mount->source);
536  free(mount->target);
537  free(mount->options);
538  free(mount);
539 }
540 
541 static void port_free(container_port_t *port)
542 {
543  free(port->source);
544  free(port->target);
545  free(port);
546 }
547 
548 gboolean
550 {
551  const char *value = NULL;
552  xmlNode *xml_obj = NULL;
553  xmlNode *xml_resource = NULL;
554  container_variant_data_t *container_data = NULL;
555 
556  CRM_ASSERT(rsc != NULL);
557  pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
558 
559  container_data = calloc(1, sizeof(container_variant_data_t));
560  rsc->variant_opaque = container_data;
561  container_data->prefix = strdup(rsc->id);
562 
563  xml_obj = first_named_child(rsc->xml, "docker");
564  if(xml_obj == NULL) {
565  return FALSE;
566  }
567 
568  value = crm_element_value(xml_obj, "masters");
569  container_data->masters = crm_parse_int(value, "0");
570  if (container_data->masters < 0) {
571  pe_err("'masters' for %s must be nonnegative integer, using 0",
572  rsc->id);
573  container_data->masters = 0;
574  }
575 
576  value = crm_element_value(xml_obj, "replicas");
577  if ((value == NULL) && (container_data->masters > 0)) {
578  container_data->replicas = container_data->masters;
579  } else {
580  container_data->replicas = crm_parse_int(value, "1");
581  }
582  if (container_data->replicas < 1) {
583  pe_err("'replicas' for %s must be positive integer, using 1", rsc->id);
584  container_data->replicas = 1;
585  }
586 
587  /*
588  * Communication between containers on the same host via the
589  * floating IPs only works if docker is started with:
590  * --userland-proxy=false --ip-masq=false
591  */
592  value = crm_element_value(xml_obj, "replicas-per-host");
593  container_data->replicas_per_host = crm_parse_int(value, "1");
594  if (container_data->replicas_per_host < 1) {
595  pe_err("'replicas-per-host' for %s must be positive integer, using 1",
596  rsc->id);
597  container_data->replicas_per_host = 1;
598  }
599  if (container_data->replicas_per_host == 1) {
601  }
602 
603  container_data->docker_run_command = crm_element_value_copy(xml_obj, "run-command");
604  container_data->docker_run_options = crm_element_value_copy(xml_obj, "options");
605  container_data->image = crm_element_value_copy(xml_obj, "image");
606  container_data->docker_network = crm_element_value_copy(xml_obj, "network");
607 
608  xml_obj = first_named_child(rsc->xml, "network");
609  if(xml_obj) {
610 
611  container_data->ip_range_start = crm_element_value_copy(xml_obj, "ip-range-start");
612  container_data->host_netmask = crm_element_value_copy(xml_obj, "host-netmask");
613  container_data->host_network = crm_element_value_copy(xml_obj, "host-interface");
614  container_data->control_port = crm_element_value_copy(xml_obj, "control-port");
615 
616  for (xmlNode *xml_child = __xml_first_child_element(xml_obj); xml_child != NULL;
617  xml_child = __xml_next_element(xml_child)) {
618 
619  container_port_t *port = calloc(1, sizeof(container_port_t));
620  port->source = crm_element_value_copy(xml_child, "port");
621 
622  if(port->source == NULL) {
623  port->source = crm_element_value_copy(xml_child, "range");
624  } else {
625  port->target = crm_element_value_copy(xml_child, "internal-port");
626  }
627 
628  if(port->source != NULL && strlen(port->source) > 0) {
629  if(port->target == NULL) {
630  port->target = strdup(port->source);
631  }
632  container_data->ports = g_list_append(container_data->ports, port);
633 
634  } else {
635  pe_err("Invalid port directive %s", ID(xml_child));
636  port_free(port);
637  }
638  }
639  }
640 
641  xml_obj = first_named_child(rsc->xml, "storage");
642  for (xmlNode *xml_child = __xml_first_child_element(xml_obj); xml_child != NULL;
643  xml_child = __xml_next_element(xml_child)) {
644 
645  container_mount_t *mount = calloc(1, sizeof(container_mount_t));
646  mount->source = crm_element_value_copy(xml_child, "source-dir");
647 
648  if(mount->source == NULL) {
649  mount->source = crm_element_value_copy(xml_child, "source-dir-root");
650  mount->flags = 1;
651  }
652  mount->target = crm_element_value_copy(xml_child, "target-dir");
653  mount->options = crm_element_value_copy(xml_child, "options");
654 
655  if(mount->source && mount->target) {
656  container_data->mounts = g_list_append(container_data->mounts, mount);
657  } else {
658  pe_err("Invalid mount directive %s", ID(xml_child));
659  mount_free(mount);
660  }
661  }
662 
663  xml_obj = first_named_child(rsc->xml, "primitive");
664  if (xml_obj && valid_network(container_data)) {
665  char *value = NULL;
666  xmlNode *xml_set = NULL;
667 
668  if(container_data->masters > 0) {
669  xml_resource = create_xml_node(NULL, XML_CIB_TAG_MASTER);
670 
671  } else {
672  xml_resource = create_xml_node(NULL, XML_CIB_TAG_INCARNATION);
673  }
674 
675  crm_xml_set_id(xml_resource, "%s-%s", container_data->prefix, xml_resource->name);
676 
677  xml_set = create_xml_node(xml_resource, XML_TAG_META_SETS);
678  crm_xml_set_id(xml_set, "%s-%s-meta", container_data->prefix, xml_resource->name);
679 
680  create_nvp(xml_set, XML_RSC_ATTR_ORDERED, "true");
681 
682  value = crm_itoa(container_data->replicas);
683  create_nvp(xml_set, XML_RSC_ATTR_INCARNATION_MAX, value);
684  free(value);
685 
686  value = crm_itoa(container_data->replicas_per_host);
687  create_nvp(xml_set, XML_RSC_ATTR_INCARNATION_NODEMAX, value);
688  free(value);
689 
690  if(container_data->replicas_per_host > 1) {
691  create_nvp(xml_set, XML_RSC_ATTR_UNIQUE, "true");
692  } else {
693  create_nvp(xml_set, XML_RSC_ATTR_UNIQUE, "false");
694  }
695 
696  if(container_data->masters) {
697  value = crm_itoa(container_data->masters);
698  create_nvp(xml_set, XML_RSC_ATTR_MASTER_MAX, value);
699  free(value);
700  }
701 
702  //crm_xml_add(xml_obj, XML_ATTR_ID, container_data->prefix);
703  add_node_copy(xml_resource, xml_obj);
704 
705  } else if(xml_obj) {
706  pe_err("Cannot control %s inside %s without either ip-range-start or control-port",
707  rsc->id, ID(xml_obj));
708  return FALSE;
709  }
710 
711  if(xml_resource) {
712  int lpc = 0;
713  GListPtr childIter = NULL;
714  resource_t *new_rsc = NULL;
715  container_mount_t *mount = NULL;
716  container_port_t *port = NULL;
717 
718  int offset = 0, max = 1024;
719  char *buffer = NULL;
720 
721  if (common_unpack(xml_resource, &new_rsc, rsc, data_set) == FALSE) {
722  pe_err("Failed unpacking resource %s", ID(rsc->xml));
723  if (new_rsc != NULL && new_rsc->fns != NULL) {
724  new_rsc->fns->free(new_rsc);
725  }
726  return FALSE;
727  }
728 
729  container_data->child = new_rsc;
730 
731  mount = calloc(1, sizeof(container_mount_t));
732  mount->source = strdup(DEFAULT_REMOTE_KEY_LOCATION);
733  mount->target = strdup(DEFAULT_REMOTE_KEY_LOCATION);
734  mount->options = NULL;
735  mount->flags = 0;
736  container_data->mounts = g_list_append(container_data->mounts, mount);
737 
738  mount = calloc(1, sizeof(container_mount_t));
739  mount->source = strdup(CRM_LOG_DIR "/bundles");
740  mount->target = strdup("/var/log");
741  mount->options = NULL;
742  mount->flags = 1;
743  container_data->mounts = g_list_append(container_data->mounts, mount);
744 
745  port = calloc(1, sizeof(container_port_t));
746  if(container_data->control_port) {
747  port->source = strdup(container_data->control_port);
748  } else {
749  port->source = crm_itoa(DEFAULT_REMOTE_PORT);
750  }
751  port->target = strdup(port->source);
752  container_data->ports = g_list_append(container_data->ports, port);
753 
754  buffer = calloc(1, max+1);
755  for(childIter = container_data->child->children; childIter != NULL; childIter = childIter->next) {
756  container_grouping_t *tuple = calloc(1, sizeof(container_grouping_t));
757  tuple->child = childIter->data;
758  tuple->offset = lpc++;
759 
760  offset += allocate_ip(container_data, tuple, buffer+offset, max-offset);
761  container_data->tuples = g_list_append(container_data->tuples, tuple);
762  }
763  container_data->docker_host_options = buffer;
764 
765  } else {
766  // Just a naked container, no pacemaker-remote
767  int offset = 0, max = 1024;
768  char *buffer = calloc(1, max+1);
769 
770  for(int lpc = 0; lpc < container_data->replicas; lpc++) {
771  container_grouping_t *tuple = calloc(1, sizeof(container_grouping_t));
772  tuple->offset = lpc;
773  offset += allocate_ip(container_data, tuple, buffer+offset, max-offset);
774  container_data->tuples = g_list_append(container_data->tuples, tuple);
775  }
776 
777  container_data->docker_host_options = buffer;
778  }
779 
780 
781  for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
782  container_grouping_t *tuple = (container_grouping_t *)gIter->data;
783  // TODO: Remove from list if create_container() returns TRUE
784  create_container(rsc, container_data, tuple, data_set);
785  }
786 
787  if(container_data->child) {
788  rsc->children = g_list_append(rsc->children, container_data->child);
789  }
790  return TRUE;
791 }
792 
793 static int
794 tuple_rsc_active(resource_t *rsc, gboolean all)
795 {
796  if (rsc) {
797  gboolean child_active = rsc->fns->active(rsc, all);
798 
799  if (child_active && !all) {
800  return TRUE;
801  } else if (!child_active && all) {
802  return FALSE;
803  }
804  }
805  return -1;
806 }
807 
808 gboolean
809 container_active(resource_t * rsc, gboolean all)
810 {
811  container_variant_data_t *container_data = NULL;
812  GListPtr iter = NULL;
813 
814  get_container_variant_data(container_data, rsc);
815  for (iter = container_data->tuples; iter != NULL; iter = iter->next) {
816  container_grouping_t *tuple = (container_grouping_t *)(iter->data);
817  int rsc_active;
818 
819  rsc_active = tuple_rsc_active(tuple->ip, all);
820  if (rsc_active >= 0) {
821  return (gboolean) rsc_active;
822  }
823 
824  rsc_active = tuple_rsc_active(tuple->child, all);
825  if (rsc_active >= 0) {
826  return (gboolean) rsc_active;
827  }
828 
829  rsc_active = tuple_rsc_active(tuple->docker, all);
830  if (rsc_active >= 0) {
831  return (gboolean) rsc_active;
832  }
833 
834  rsc_active = tuple_rsc_active(tuple->remote, all);
835  if (rsc_active >= 0) {
836  return (gboolean) rsc_active;
837  }
838  }
839 
840  /* If "all" is TRUE, we've already checked that no resources were inactive,
841  * so return TRUE; if "all" is FALSE, we didn't find any active resources,
842  * so return FALSE.
843  */
844  return all;
845 }
846 
847 resource_t *
848 find_container_child(const char *stem, resource_t * rsc, node_t *node)
849 {
850  container_variant_data_t *container_data = NULL;
851  resource_t *parent = uber_parent(rsc);
852  CRM_ASSERT(parent->parent);
853 
854  parent = parent->parent;
855  get_container_variant_data(container_data, parent);
856 
857  if (is_not_set(rsc->flags, pe_rsc_unique)) {
858  for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
859  container_grouping_t *tuple = (container_grouping_t *)gIter->data;
860 
861  CRM_ASSERT(tuple);
862  if(tuple->node->details == node->details) {
863  rsc = tuple->child;
864  break;
865  }
866  }
867  }
868 
869  if (rsc && safe_str_neq(stem, rsc->id)) {
870  free(rsc->clone_name);
871  rsc->clone_name = strdup(stem);
872  }
873 
874  return rsc;
875 }
876 
877 static void
878 print_rsc_in_list(resource_t *rsc, const char *pre_text, long options,
879  void *print_data)
880 {
881  if (rsc != NULL) {
882  if (options & pe_print_html) {
883  status_print("<li>");
884  }
885  rsc->fns->print(rsc, pre_text, options, print_data);
886  if (options & pe_print_html) {
887  status_print("</li>\n");
888  }
889  }
890 }
891 
892 static void
893 container_print_xml(resource_t * rsc, const char *pre_text, long options, void *print_data)
894 {
895  container_variant_data_t *container_data = NULL;
896  char *child_text = NULL;
897  CRM_CHECK(rsc != NULL, return);
898 
899  if (pre_text == NULL) {
900  pre_text = "";
901  }
902  child_text = crm_concat(pre_text, " ", ' ');
903 
904  get_container_variant_data(container_data, rsc);
905 
906  status_print("%s<bundle ", pre_text);
907  status_print("id=\"%s\" ", rsc->id);
908  status_print("type=\"docker\" ");
909  status_print("image=\"%s\" ", container_data->image);
910  status_print("unique=\"%s\" ", is_set(rsc->flags, pe_rsc_unique)? "true" : "false");
911  status_print("managed=\"%s\" ", is_set(rsc->flags, pe_rsc_managed) ? "true" : "false");
912  status_print("failed=\"%s\" ", is_set(rsc->flags, pe_rsc_failed) ? "true" : "false");
913  status_print(">\n");
914 
915  for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
916  container_grouping_t *tuple = (container_grouping_t *)gIter->data;
917 
918  CRM_ASSERT(tuple);
919  status_print("%s <replica id=\"%d\">\n", pre_text, tuple->offset);
920  print_rsc_in_list(tuple->ip, child_text, options, print_data);
921  print_rsc_in_list(tuple->child, child_text, options, print_data);
922  print_rsc_in_list(tuple->docker, child_text, options, print_data);
923  print_rsc_in_list(tuple->remote, child_text, options, print_data);
924  status_print("%s </replica>\n", pre_text);
925  }
926  status_print("%s</bundle>\n", pre_text);
927  free(child_text);
928 }
929 
930 static void
931 tuple_print(container_grouping_t * tuple, const char *pre_text, long options, void *print_data)
932 {
933  node_t *node = NULL;
934  resource_t *rsc = tuple->child;
935 
936  int offset = 0;
937  char buffer[LINE_MAX];
938 
939  if(rsc == NULL) {
940  rsc = tuple->docker;
941  }
942 
943  if(tuple->remote) {
944  offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", rsc_printable_id(tuple->remote));
945  } else {
946  offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", rsc_printable_id(tuple->docker));
947  }
948  if(tuple->ipaddr) {
949  offset += snprintf(buffer + offset, LINE_MAX - offset, " (%s)", tuple->ipaddr);
950  }
951 
952  if(tuple->docker && tuple->docker->running_on != NULL) {
953  node = tuple->docker->running_on->data;
954  } else if (tuple->docker == NULL && rsc->running_on != NULL) {
955  node = rsc->running_on->data;
956  }
957  common_print(rsc, pre_text, buffer, node, options, print_data);
958 }
959 
960 void
961 container_print(resource_t * rsc, const char *pre_text, long options, void *print_data)
962 {
963  container_variant_data_t *container_data = NULL;
964  char *child_text = NULL;
965  CRM_CHECK(rsc != NULL, return);
966 
967  if (options & pe_print_xml) {
968  container_print_xml(rsc, pre_text, options, print_data);
969  return;
970  }
971 
972  get_container_variant_data(container_data, rsc);
973 
974  if (pre_text == NULL) {
975  pre_text = " ";
976  }
977 
978  status_print("%sDocker container%s: %s [%s]%s%s\n",
979  pre_text, container_data->replicas>1?" set":"", rsc->id, container_data->image,
980  is_set(rsc->flags, pe_rsc_unique) ? " (unique)" : "",
981  is_set(rsc->flags, pe_rsc_managed) ? "" : " (unmanaged)");
982  if (options & pe_print_html) {
983  status_print("<br />\n<ul>\n");
984  }
985 
986 
987  for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
988  container_grouping_t *tuple = (container_grouping_t *)gIter->data;
989 
990  CRM_ASSERT(tuple);
991  if (options & pe_print_html) {
992  status_print("<li>");
993  }
994 
995  if(is_set(options, pe_print_clone_details)) {
996  child_text = crm_strdup_printf(" %s", pre_text);
997  if(g_list_length(container_data->tuples) > 1) {
998  status_print(" %sReplica[%d]\n", pre_text, tuple->offset);
999  }
1000  if (options & pe_print_html) {
1001  status_print("<br />\n<ul>\n");
1002  }
1003  print_rsc_in_list(tuple->ip, child_text, options, print_data);
1004  print_rsc_in_list(tuple->docker, child_text, options, print_data);
1005  print_rsc_in_list(tuple->remote, child_text, options, print_data);
1006  print_rsc_in_list(tuple->child, child_text, options, print_data);
1007  if (options & pe_print_html) {
1008  status_print("</ul>\n");
1009  }
1010  } else {
1011  child_text = crm_strdup_printf("%s ", pre_text);
1012  tuple_print(tuple, child_text, options, print_data);
1013  }
1014  free(child_text);
1015 
1016  if (options & pe_print_html) {
1017  status_print("</li>\n");
1018  }
1019  }
1020  if (options & pe_print_html) {
1021  status_print("</ul>\n");
1022  }
1023 }
1024 
1025 void
1026 tuple_free(container_grouping_t *tuple)
1027 {
1028  if(tuple == NULL) {
1029  return;
1030  }
1031 
1032  if(tuple->node) {
1033  free(tuple->node);
1034  tuple->node = NULL;
1035  }
1036 
1037  if(tuple->ip) {
1038  free_xml(tuple->ip->xml);
1039  tuple->ip->xml = NULL;
1040  tuple->ip->fns->free(tuple->ip);
1041  tuple->ip = NULL;
1042  }
1043  if(tuple->docker) {
1044  free_xml(tuple->docker->xml);
1045  tuple->docker->xml = NULL;
1046  tuple->docker->fns->free(tuple->docker);
1047  tuple->docker = NULL;
1048  }
1049  if(tuple->remote) {
1050  free_xml(tuple->remote->xml);
1051  tuple->remote->xml = NULL;
1052  tuple->remote->fns->free(tuple->remote);
1053  tuple->remote = NULL;
1054  }
1055  free(tuple->ipaddr);
1056  free(tuple);
1057 }
1058 
1059 void
1061 {
1062  container_variant_data_t *container_data = NULL;
1063  CRM_CHECK(rsc != NULL, return);
1064 
1065  get_container_variant_data(container_data, rsc);
1066  pe_rsc_trace(rsc, "Freeing %s", rsc->id);
1067 
1068  free(container_data->prefix);
1069  free(container_data->image);
1070  free(container_data->control_port);
1071  free(container_data->host_network);
1072  free(container_data->host_netmask);
1073  free(container_data->ip_range_start);
1074  free(container_data->docker_network);
1075  free(container_data->docker_run_options);
1076  free(container_data->docker_run_command);
1077  free(container_data->docker_host_options);
1078 
1079  g_list_free_full(container_data->tuples, (GDestroyNotify)tuple_free);
1080  g_list_free_full(container_data->mounts, (GDestroyNotify)mount_free);
1081  g_list_free_full(container_data->ports, (GDestroyNotify)port_free);
1082  g_list_free(rsc->children);
1083 
1084  if(container_data->child) {
1085  free_xml(container_data->child->xml);
1086  container_data->child->xml = NULL;
1087  container_data->child->fns->free(container_data->child);
1088  }
1089  common_free(rsc);
1090 }
1091 
1092 enum rsc_role_e
1093 container_resource_state(const resource_t * rsc, gboolean current)
1094 {
1095  enum rsc_role_e container_role = RSC_ROLE_UNKNOWN;
1096  return container_role;
1097 }
bool remote_id_conflict(const char *remote_name, pe_working_set_t *data)
Definition: unpack.c:392
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:164
GListPtr nodes
Definition: status.h:102
xmlNode * xml
Definition: status.h:254
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:150
#define INFINITY
Definition: crm.h:83
node_t * node_copy(const node_t *this_node)
Definition: utils.c:79
int weight
Definition: status.h:170
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:328
#define XML_ATTR_TYPE
Definition: msg_xml.h:104
void(* free)(resource_t *)
Definition: complex.h:51
gboolean common_unpack(xmlNode *xml_obj, resource_t **rsc, resource_t *parent, pe_working_set_t *data_set)
Definition: complex.c:426
void common_free(resource_t *rsc)
Definition: complex.c:855
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:271
#define status_print(fmt, args...)
Definition: unpack.h:79
int crm_parse_int(const char *text, const char *default_text)
Definition: strings.c:125
char * crm_element_value_copy(xmlNode *data, const char *name)
Definition: xml.c:3893
GListPtr resources
Definition: status.h:103
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:354
node_t * pe_find_node(GListPtr node_list, const char *uname)
Definition: status.c:298
char * clone_name
Definition: status.h:253
resource_t * uber_parent(resource_t *rsc)
Definition: complex.c:841
#define clear_bit(word, bit)
Definition: crm_internal.h:193
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:174
#define XML_RSC_ATTR_INCARNATION_MAX
Definition: msg_xml.h:204
GListPtr children
Definition: status.h:296
#define pe_rsc_allow_remote_remotes
Definition: status.h:196
void crm_xml_sanitize_id(char *id)
Sanitize a string so it is usable as an XML ID.
Definition: xml.c:3045
char * id
Definition: status.h:252
#define DEFAULT_REMOTE_PORT
Definition: lrmd.h:43
#define DEFAULT_REMOTE_KEY_LOCATION
Definition: lrmd.h:41
#define CRM_LOG_DIR
Definition: config.h:59
#define XML_TAG_ATTR_SETS
Definition: msg_xml.h:177
char uname[MAX_NAME]
Definition: internal.h:53
gboolean is_remote_node(node_t *node)
Definition: remote.c:62
struct node_shared_s * details
Definition: status.h:173
#define set_bit(word, bit)
Definition: crm_internal.h:192
#define XML_RSC_ATTR_CONTAINER
Definition: msg_xml.h:221
#define XML_ATTR_ID
Definition: msg_xml.h:101
#define XML_CIB_TAG_RESOURCE
Definition: msg_xml.h:188
#define pe_rsc_failed
Definition: status.h:198
resource_object_functions_t * fns
Definition: status.h:261
GHashTable * allowed_nodes
Definition: status.h:287
void * variant_opaque
Definition: status.h:259
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition: xml.c:2404
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:244
#define XML_RSC_ATTR_ORDERED
Definition: msg_xml.h:201
#define XML_TAG_META_SETS
Definition: msg_xml.h:178
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:2587
const char * crm_element_value(xmlNode *data, const char *name)
Definition: xml.c:5134
unsigned long long flags
Definition: status.h:276
#define XML_RSC_ATTR_INCARNATION_NODEMAX
Definition: msg_xml.h:206
resource_t * parent
Definition: status.h:258
void free_xml(xmlNode *child)
Definition: xml.c:2705
#define XML_RSC_ATTR_UNIQUE
Definition: msg_xml.h:212
gboolean(* active)(resource_t *, gboolean)
Definition: complex.h:48
void common_print(resource_t *rsc, const char *pre_text, const char *name, node_t *node, long options, void *print_data)
Definition: native.c:469
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Definition: xml.c:2489
#define XML_RSC_ATTR_MASTER_MAX
Definition: msg_xml.h:207
void(* print)(resource_t *, const char *, long, void *)
Definition: complex.h:47
#define pe_rsc_unique
Definition: status.h:185
gboolean container_unpack(resource_t *rsc, pe_working_set_t *data_set)
Definition: container.c:549
#define SBIN_DIR
Definition: config.h:685
enum rsc_role_e container_resource_state(const resource_t *rsc, gboolean current)
Definition: container.c:1093
void tuple_free(container_grouping_t *tuple)
Definition: container.c:1026
#define XML_CIB_TAG_INCARNATION
Definition: msg_xml.h:190
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition: common.c:419
void crm_xml_set_id(xmlNode *xml, const char *format,...) __attribute__((__format__(__printf__
gboolean container_active(resource_t *rsc, gboolean all)
Definition: container.c:809
#define pe_rsc_managed
Definition: status.h:180
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:355
#define CRM_ASSERT(expr)
Definition: error.h:35
char data[0]
Definition: internal.h:58
rsc_role_e
Definition: common.h:81
#define XML_CIB_TAG_MASTER
Definition: msg_xml.h:191
xmlNode * first_named_child(xmlNode *parent, const char *name)
Definition: xml.c:5046
Definition: status.h:169
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:25
char * crm_concat(const char *prefix, const char *suffix, char join)
Definition: strings.c:32
#define ID(x)
Definition: msg_xml.h:434
#define pe_err(fmt...)
Definition: internal.h:27
char * crm_itoa(int an_int)
Definition: strings.c:60
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
void container_print(resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: container.c:961
GList * GListPtr
Definition: crm.h:202
resource_t * find_container_child(const char *stem, resource_t *rsc, node_t *node)
Definition: container.c:848
const char * rsc_printable_id(resource_t *rsc)
Definition: utils.c:1829
#define XML_OP_ATTR_ALLOW_MIGRATE
Definition: msg_xml.h:232
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:243
void container_free(resource_t *rsc)
Definition: container.c:1060
GListPtr running_on
Definition: status.h:285