pacemaker  2.1.5-b7adf64e51
Scalable High-Availability cluster resource manager
group.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2022 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 <stdint.h>
13 
14 #include <crm/pengine/rules.h>
15 #include <crm/pengine/status.h>
16 #include <crm/pengine/internal.h>
17 #include <crm/msg_xml.h>
18 #include <crm/common/output.h>
21 #include <pe_status_private.h>
22 
23 typedef struct group_variant_data_s {
24  pe_resource_t *last_child; // Last group member
25  uint32_t flags; // Group of enum pe__group_flags
27 
38 {
39  if (group != NULL) {
40  CRM_CHECK((group->variant == pe_group)
41  && (group->variant_opaque != NULL), return NULL);
42  return ((group_variant_data_t *) group->variant_opaque)->last_child;
43  }
44  return NULL;
45 }
46 
56 bool
57 pe__group_flag_is_set(const pe_resource_t *group, uint32_t flags)
58 {
59  group_variant_data_t *group_data = NULL;
60 
61  CRM_CHECK((group != NULL) && (group->variant == pe_group)
62  && (group->variant_opaque != NULL), return false);
63  group_data = (group_variant_data_t *) group->variant_opaque;
64  return pcmk_all_flags_set(group_data->flags, flags);
65 }
66 
76 static void
77 set_group_flag(pe_resource_t *group, const char *option, uint32_t flag,
78  uint32_t wo_bit)
79 {
80  const char *value_s = NULL;
81  int value = 0;
82 
83  value_s = g_hash_table_lookup(group->meta, option);
84 
85  // We don't actually need the null check but it speeds up the common case
86  if ((value_s == NULL) || (crm_str_to_boolean(value_s, &value) < 0)
87  || (value != 0)) {
88 
89  ((group_variant_data_t *) group->variant_opaque)->flags |= flag;
90 
91  } else {
92  pe_warn_once(wo_bit,
93  "Support for the '%s' group meta-attribute is deprecated "
94  "and will be removed in a future release "
95  "(use a resource set instead)", option);
96  }
97 }
98 
99 static int
100 inactive_resources(pe_resource_t *rsc)
101 {
102  int retval = 0;
103 
104  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
105  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
106 
107  if (!child_rsc->fns->active(child_rsc, TRUE)) {
108  retval++;
109  }
110  }
111 
112  return retval;
113 }
114 
115 static void
116 group_header(pcmk__output_t *out, int *rc, pe_resource_t *rsc, int n_inactive, bool show_inactive)
117 {
118  GString *attrs = NULL;
119 
120  if (n_inactive > 0 && !show_inactive) {
121  attrs = g_string_sized_new(64);
122  g_string_append_printf(attrs, "%d member%s inactive", n_inactive,
123  pcmk__plural_s(n_inactive));
124  }
125 
126  if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
127  pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
128  }
129 
130  if (pe__resource_is_disabled(rsc)) {
131  pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
132  }
133 
134  if (attrs != NULL) {
135  PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s (%s)",
136  rsc->id, (const char *) attrs->str);
137  g_string_free(attrs, TRUE);
138  } else {
139  PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s", rsc->id);
140  }
141 }
142 
143 static bool
144 skip_child_rsc(pe_resource_t *rsc, pe_resource_t *child, gboolean parent_passes,
145  GList *only_rsc, uint32_t show_opts)
146 {
147  bool star_list = pcmk__list_of_1(only_rsc) &&
148  pcmk__str_eq("*", g_list_first(only_rsc)->data, pcmk__str_none);
149  bool child_filtered = child->fns->is_filtered(child, only_rsc, FALSE);
150  bool child_active = child->fns->active(child, FALSE);
151  bool show_inactive = pcmk_is_set(show_opts, pcmk_show_inactive_rscs);
152 
153  /* If the resource is in only_rsc by name (so, ignoring "*") then allow
154  * it regardless of if it's active or not.
155  */
156  if (!star_list && !child_filtered) {
157  return false;
158 
159  } else if (!child_filtered && (child_active || show_inactive)) {
160  return false;
161 
162  } else if (parent_passes && (child_active || show_inactive)) {
163  return false;
164 
165  }
166 
167  return true;
168 }
169 
170 gboolean
172 {
173  xmlNode *xml_obj = rsc->xml;
174  xmlNode *xml_native_rsc = NULL;
175  group_variant_data_t *group_data = NULL;
176  const char *clone_id = NULL;
177 
178  pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
179 
180  group_data = calloc(1, sizeof(group_variant_data_t));
181  group_data->last_child = NULL;
182  rsc->variant_opaque = group_data;
183 
184  // @COMPAT These are deprecated since 2.1.5
185  set_group_flag(rsc, XML_RSC_ATTR_ORDERED, pe__group_ordered,
187  set_group_flag(rsc, "collocated", pe__group_colocated, pe_wo_group_coloc);
188 
190 
191  for (xml_native_rsc = pcmk__xe_first_child(xml_obj); xml_native_rsc != NULL;
192  xml_native_rsc = pcmk__xe_next(xml_native_rsc)) {
193 
194  if (pcmk__str_eq((const char *)xml_native_rsc->name,
196  pe_resource_t *new_rsc = NULL;
197 
198  crm_xml_add(xml_native_rsc, XML_RSC_ATTR_INCARNATION, clone_id);
199  if (pe__unpack_resource(xml_native_rsc, &new_rsc, rsc,
200  data_set) != pcmk_rc_ok) {
201  continue;
202  }
203 
204  rsc->children = g_list_append(rsc->children, new_rsc);
205  group_data->last_child = new_rsc;
206  pe_rsc_trace(rsc, "Added %s member %s", rsc->id, new_rsc->id);
207  }
208  }
209 
210  if (rsc->children == NULL) {
211  /* The schema does not allow empty groups, but if validation is
212  * disabled, we allow them (members can be added later).
213  *
214  * @COMPAT At a major release bump, we should consider this a failure so
215  * that group methods can assume children is not NULL, and there
216  * are no strange effects from phantom groups due to their
217  * presence or meta-attributes.
218  */
219  pcmk__config_warn("Group %s will be ignored because it does not have "
220  "any members", rsc->id);
221  }
222  return TRUE;
223 }
224 
225 gboolean
226 group_active(pe_resource_t * rsc, gboolean all)
227 {
228  gboolean c_all = TRUE;
229  gboolean c_any = FALSE;
230  GList *gIter = rsc->children;
231 
232  for (; gIter != NULL; gIter = gIter->next) {
233  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
234 
235  if (child_rsc->fns->active(child_rsc, all)) {
236  c_any = TRUE;
237  } else {
238  c_all = FALSE;
239  }
240  }
241 
242  if (c_any == FALSE) {
243  return FALSE;
244  } else if (all && c_all == FALSE) {
245  return FALSE;
246  }
247  return TRUE;
248 }
249 
254 static void
255 group_print_xml(pe_resource_t *rsc, const char *pre_text, long options,
256  void *print_data)
257 {
258  GList *gIter = rsc->children;
259  char *child_text = crm_strdup_printf("%s ", pre_text);
260 
261  status_print("%s<group id=\"%s\" ", pre_text, rsc->id);
262  status_print("number_resources=\"%d\" ", g_list_length(rsc->children));
263  status_print(">\n");
264 
265  for (; gIter != NULL; gIter = gIter->next) {
266  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
267 
268  child_rsc->fns->print(child_rsc, child_text, options, print_data);
269  }
270 
271  status_print("%s</group>\n", pre_text);
272  free(child_text);
273 }
274 
279 void
280 group_print(pe_resource_t *rsc, const char *pre_text, long options,
281  void *print_data)
282 {
283  char *child_text = NULL;
284  GList *gIter = rsc->children;
285 
286  if (pre_text == NULL) {
287  pre_text = " ";
288  }
289 
290  if (options & pe_print_xml) {
291  group_print_xml(rsc, pre_text, options, print_data);
292  return;
293  }
294 
295  child_text = crm_strdup_printf("%s ", pre_text);
296 
297  status_print("%sResource Group: %s", pre_text ? pre_text : "", rsc->id);
298 
299  if (options & pe_print_html) {
300  status_print("\n<ul>\n");
301 
302  } else if ((options & pe_print_log) == 0) {
303  status_print("\n");
304  }
305 
306  if (options & pe_print_brief) {
307  print_rscs_brief(rsc->children, child_text, options, print_data, TRUE);
308 
309  } else {
310  for (; gIter != NULL; gIter = gIter->next) {
311  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
312 
313  if (options & pe_print_html) {
314  status_print("<li>\n");
315  }
316  child_rsc->fns->print(child_rsc, child_text, options, print_data);
317  if (options & pe_print_html) {
318  status_print("</li>\n");
319  }
320  }
321  }
322 
323  if (options & pe_print_html) {
324  status_print("</ul>\n");
325  }
326  free(child_text);
327 }
328 
329 PCMK__OUTPUT_ARGS("group", "uint32_t", "pe_resource_t *", "GList *", "GList *")
330 int
331 pe__group_xml(pcmk__output_t *out, va_list args)
332 {
333  uint32_t show_opts = va_arg(args, uint32_t);
334  pe_resource_t *rsc = va_arg(args, pe_resource_t *);
335  GList *only_node = va_arg(args, GList *);
336  GList *only_rsc = va_arg(args, GList *);
337 
338  GList *gIter = rsc->children;
339  char *count = pcmk__itoa(g_list_length(gIter));
340 
341  int rc = pcmk_rc_no_output;
342 
343  gboolean parent_passes = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
344  (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
345 
346  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
347  free(count);
348  return rc;
349  }
350 
351  for (; gIter != NULL; gIter = gIter->next) {
352  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
353 
354  if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
355  continue;
356  }
357 
358  if (rc == pcmk_rc_no_output) {
359  rc = pe__name_and_nvpairs_xml(out, true, "group", 4
360  , "id", rsc->id
361  , "number_resources", count
362  , "managed", pe__rsc_bool_str(rsc, pe_rsc_managed)
363  , "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)));
364  free(count);
365  CRM_ASSERT(rc == pcmk_rc_ok);
366  }
367 
368  out->message(out, crm_map_element_name(child_rsc->xml), show_opts, child_rsc,
369  only_node, only_rsc);
370  }
371 
372  if (rc == pcmk_rc_ok) {
374  }
375 
376  return rc;
377 }
378 
379 PCMK__OUTPUT_ARGS("group", "uint32_t", "pe_resource_t *", "GList *", "GList *")
380 int
382 {
383  uint32_t show_opts = va_arg(args, uint32_t);
384  pe_resource_t *rsc = va_arg(args, pe_resource_t *);
385  GList *only_node = va_arg(args, GList *);
386  GList *only_rsc = va_arg(args, GList *);
387 
388  int rc = pcmk_rc_no_output;
389 
390  gboolean parent_passes = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
391  (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
392 
393  gboolean active = rsc->fns->active(rsc, TRUE);
394  gboolean partially_active = rsc->fns->active(rsc, FALSE);
395 
396  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
397  return rc;
398  }
399 
400  if (pcmk_is_set(show_opts, pcmk_show_brief)) {
401  GList *rscs = pe__filter_rsc_list(rsc->children, only_rsc);
402 
403  if (rscs != NULL) {
404  group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
406  pe__rscs_brief_output(out, rscs, show_opts | pcmk_show_inactive_rscs);
407 
408  rc = pcmk_rc_ok;
409  g_list_free(rscs);
410  }
411 
412  } else {
413  for (GList *gIter = rsc->children; gIter; gIter = gIter->next) {
414  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
415 
416  if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
417  continue;
418  }
419 
420  group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
422  out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
423  child_rsc, only_node, only_rsc);
424  }
425  }
426 
427  PCMK__OUTPUT_LIST_FOOTER(out, rc);
428 
429  return rc;
430 }
431 
432 void
434 {
435  CRM_CHECK(rsc != NULL, return);
436 
437  pe_rsc_trace(rsc, "Freeing %s", rsc->id);
438 
439  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
440  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
441 
442  CRM_ASSERT(child_rsc);
443  pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
444  child_rsc->fns->free(child_rsc);
445  }
446 
447  pe_rsc_trace(rsc, "Freeing child list");
448  g_list_free(rsc->children);
449 
450  common_free(rsc);
451 }
452 
453 enum rsc_role_e
454 group_resource_state(const pe_resource_t * rsc, gboolean current)
455 {
456  enum rsc_role_e group_role = RSC_ROLE_UNKNOWN;
457  GList *gIter = rsc->children;
458 
459  for (; gIter != NULL; gIter = gIter->next) {
460  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
461  enum rsc_role_e role = child_rsc->fns->state(child_rsc, current);
462 
463  if (role > group_role) {
464  group_role = role;
465  }
466  }
467 
468  pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(group_role));
469  return group_role;
470 }
471 
472 gboolean
473 pe__group_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
474 {
475  gboolean passes = FALSE;
476 
477  if (check_parent && pcmk__str_in_list(rsc_printable_id(uber_parent(rsc)), only_rsc, pcmk__str_star_matches)) {
478  passes = TRUE;
479  } else if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
480  passes = TRUE;
481  } else if (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
482  passes = TRUE;
483  } else {
484  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
485  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
486 
487  if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
488  passes = TRUE;
489  break;
490  }
491  }
492  }
493 
494  return !passes;
495 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
int pe__group_xml(pcmk__output_t *out, va_list args)
Definition: group.c:331
char data[0]
Definition: cpg.c:55
Control output from tools.
#define pcmk__config_warn(fmt...)
GList * children
Definition: pe_types.h:384
xmlNode * xml
Definition: pe_types.h:331
#define XML_RSC_ATTR_INCARNATION
Definition: msg_xml.h:228
GHashTable * meta
Definition: pe_types.h:380
resource_object_functions_t * fns
Definition: pe_types.h:340
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:323
void print_rscs_brief(GList *rsc_list, const char *pre_text, long options, void *print_data, gboolean print_all)
Definition: native.c:1204
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition: strings.c:703
GList * pe__filter_rsc_list(GList *rscs, GList *filter)
Definition: utils.c:774
const char * role2text(enum rsc_role_e role)
Definition: common.c:454
bool pe__group_flag_is_set(const pe_resource_t *group, uint32_t flags)
Definition: group.c:57
int pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, unsigned int options)
gboolean group_unpack(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: group.c:171
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:912
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
#define XML_CIB_TAG_RESOURCE
Definition: msg_xml.h:217
#define PCMK__OUTPUT_ARGS(ARGS...)
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:544
#define pe_warn_once(pe_wo_bit, fmt...)
Definition: internal.h:172
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
void common_free(pe_resource_t *rsc)
Definition: complex.c:926
int pe__group_default(pcmk__output_t *out, va_list args)
Definition: group.c:381
unsigned long long flags
Definition: pe_types.h:355
#define XML_RSC_ATTR_ORDERED
Definition: msg_xml.h:226
pe_working_set_t * data_set
void(* print)(pe_resource_t *, const char *, long, void *)
Definition: pe_types.h:52
void(* free)(pe_resource_t *)
Definition: pe_types.h:56
enum pe_obj_types variant
Definition: pe_types.h:338
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:513
pe_resource_t * pe__last_group_member(const pe_resource_t *group)
Definition: group.c:37
gboolean pe__group_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: group.c:473
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode)
enum rsc_role_e(* state)(const pe_resource_t *, gboolean)
Definition: pe_types.h:54
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:427
void group_print(pe_resource_t *rsc, const char *pre_text, long options, void *print_data)
Definition: group.c:280
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...)
Cluster status and scheduling.
gboolean(* is_filtered)(pe_resource_t *, GList *, gboolean)
Definition: pe_types.h:58
void * variant_opaque
Definition: pe_types.h:339
#define CRM_ASSERT(expr)
Definition: results.h:42
const char * rsc_printable_id(pe_resource_t *rsc)
Definition: utils.c:568
#define status_print(fmt, args...)
enum rsc_role_e group_resource_state(const pe_resource_t *rsc, gboolean current)
Definition: group.c:454
This structure contains everything that makes up a single output formatter.
#define pcmk__plural_s(i)
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
struct group_variant_data_s group_variant_data_t
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:47
bool pe__resource_is_disabled(pe_resource_t *rsc)
Definition: utils.c:735
void group_free(pe_resource_t *rsc)
Definition: group.c:433
#define pe_rsc_managed
Definition: pe_types.h:257
gboolean group_active(pe_resource_t *rsc, gboolean all)
Definition: group.c:226
uint64_t flags
Definition: remote.c:215
int pe__unpack_resource(xmlNode *xml_obj, pe_resource_t **rsc, pe_resource_t *parent, pe_working_set_t *data_set)
Definition: complex.c:562
gboolean(* active)(pe_resource_t *, gboolean)
Definition: pe_types.h:53
char * id
Definition: pe_types.h:329
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition: strings.c:883