pacemaker  2.1.4-dc6eb4362
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 #define VARIANT_GROUP 1
24 #include "./variant.h"
25 
26 static int
27 inactive_resources(pe_resource_t *rsc)
28 {
29  int retval = 0;
30 
31  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
32  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
33 
34  if (!child_rsc->fns->active(child_rsc, TRUE)) {
35  retval++;
36  }
37  }
38 
39  return retval;
40 }
41 
42 static void
43 group_header(pcmk__output_t *out, int *rc, pe_resource_t *rsc, int n_inactive, bool show_inactive)
44 {
45  char *attrs = NULL;
46  size_t len = 0;
47 
48  if (n_inactive > 0 && !show_inactive) {
49  char *word = crm_strdup_printf("%d member%s inactive", n_inactive, pcmk__plural_s(n_inactive));
50  pcmk__add_separated_word(&attrs, &len, word, ", ");
51  free(word);
52  }
53 
54  if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
55  pcmk__add_separated_word(&attrs, &len, "unmanaged", ", ");
56  }
57 
58  if (pe__resource_is_disabled(rsc)) {
59  pcmk__add_separated_word(&attrs, &len, "disabled", ", ");
60  }
61 
62  if (attrs) {
63  PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s (%s)",
64  rsc->id, attrs);
65  free(attrs);
66  } else {
67  PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s", rsc->id);
68  }
69 }
70 
71 static bool
72 skip_child_rsc(pe_resource_t *rsc, pe_resource_t *child, gboolean parent_passes,
73  GList *only_rsc, uint32_t show_opts)
74 {
75  bool star_list = pcmk__list_of_1(only_rsc) &&
76  pcmk__str_eq("*", g_list_first(only_rsc)->data, pcmk__str_none);
77  bool child_filtered = child->fns->is_filtered(child, only_rsc, FALSE);
78  bool child_active = child->fns->active(child, FALSE);
79  bool show_inactive = pcmk_is_set(show_opts, pcmk_show_inactive_rscs);
80 
81  /* If the resource is in only_rsc by name (so, ignoring "*") then allow
82  * it regardless of if it's active or not.
83  */
84  if (!star_list && !child_filtered) {
85  return false;
86 
87  } else if (!child_filtered && (child_active || show_inactive)) {
88  return false;
89 
90  } else if (parent_passes && (child_active || show_inactive)) {
91  return false;
92 
93  }
94 
95  return true;
96 }
97 
98 gboolean
100 {
101  xmlNode *xml_obj = rsc->xml;
102  xmlNode *xml_native_rsc = NULL;
103  group_variant_data_t *group_data = NULL;
104  const char *group_ordered = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_ORDERED);
105  const char *group_colocated = g_hash_table_lookup(rsc->meta, "collocated");
106  const char *clone_id = NULL;
107 
108  pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
109 
110  group_data = calloc(1, sizeof(group_variant_data_t));
111  group_data->num_children = 0;
112  group_data->first_child = NULL;
113  group_data->last_child = NULL;
114  rsc->variant_opaque = group_data;
115 
116  // We don't actually need the null checks but it speeds up the common case
117  if ((group_ordered == NULL)
118  || (crm_str_to_boolean(group_ordered, &(group_data->ordered)) < 0)) {
119  group_data->ordered = TRUE;
120  }
121  if ((group_colocated == NULL)
122  || (crm_str_to_boolean(group_colocated, &(group_data->colocated)) < 0)) {
123  group_data->colocated = TRUE;
124  }
125 
127 
128  for (xml_native_rsc = pcmk__xe_first_child(xml_obj); xml_native_rsc != NULL;
129  xml_native_rsc = pcmk__xe_next(xml_native_rsc)) {
130 
131  if (pcmk__str_eq((const char *)xml_native_rsc->name,
133  pe_resource_t *new_rsc = NULL;
134 
135  crm_xml_add(xml_native_rsc, XML_RSC_ATTR_INCARNATION, clone_id);
136  if (common_unpack(xml_native_rsc, &new_rsc, rsc, data_set) == FALSE) {
137  pe_err("Failed unpacking resource %s", crm_element_value(xml_obj, XML_ATTR_ID));
138  if (new_rsc != NULL && new_rsc->fns != NULL) {
139  new_rsc->fns->free(new_rsc);
140  }
141  continue;
142  }
143 
144  group_data->num_children++;
145  rsc->children = g_list_append(rsc->children, new_rsc);
146 
147  if (group_data->first_child == NULL) {
148  group_data->first_child = new_rsc;
149  }
150  group_data->last_child = new_rsc;
151  pe_rsc_trace(rsc, "Added %s member %s", rsc->id, new_rsc->id);
152  }
153  }
154 
155  if (group_data->num_children == 0) {
156  pcmk__config_warn("Group %s does not have any children", rsc->id);
157  return TRUE; // Allow empty groups, children can be added later
158  }
159 
160  pe_rsc_trace(rsc, "Added %d children to resource %s...", group_data->num_children, rsc->id);
161 
162  return TRUE;
163 }
164 
165 gboolean
166 group_active(pe_resource_t * rsc, gboolean all)
167 {
168  gboolean c_all = TRUE;
169  gboolean c_any = FALSE;
170  GList *gIter = rsc->children;
171 
172  for (; gIter != NULL; gIter = gIter->next) {
173  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
174 
175  if (child_rsc->fns->active(child_rsc, all)) {
176  c_any = TRUE;
177  } else {
178  c_all = FALSE;
179  }
180  }
181 
182  if (c_any == FALSE) {
183  return FALSE;
184  } else if (all && c_all == FALSE) {
185  return FALSE;
186  }
187  return TRUE;
188 }
189 
190 static void
191 group_print_xml(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
192 {
193  GList *gIter = rsc->children;
194  char *child_text = crm_strdup_printf("%s ", pre_text);
195 
196  status_print("%s<group id=\"%s\" ", pre_text, rsc->id);
197  status_print("number_resources=\"%d\" ", g_list_length(rsc->children));
198  status_print(">\n");
199 
200  for (; gIter != NULL; gIter = gIter->next) {
201  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
202 
203  child_rsc->fns->print(child_rsc, child_text, options, print_data);
204  }
205 
206  status_print("%s</group>\n", pre_text);
207  free(child_text);
208 }
209 
210 void
211 group_print(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
212 {
213  char *child_text = NULL;
214  GList *gIter = rsc->children;
215 
216  if (pre_text == NULL) {
217  pre_text = " ";
218  }
219 
220  if (options & pe_print_xml) {
221  group_print_xml(rsc, pre_text, options, print_data);
222  return;
223  }
224 
225  child_text = crm_strdup_printf("%s ", pre_text);
226 
227  status_print("%sResource Group: %s", pre_text ? pre_text : "", rsc->id);
228 
229  if (options & pe_print_html) {
230  status_print("\n<ul>\n");
231 
232  } else if ((options & pe_print_log) == 0) {
233  status_print("\n");
234  }
235 
236  if (options & pe_print_brief) {
237  print_rscs_brief(rsc->children, child_text, options, print_data, TRUE);
238 
239  } else {
240  for (; gIter != NULL; gIter = gIter->next) {
241  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
242 
243  if (options & pe_print_html) {
244  status_print("<li>\n");
245  }
246  child_rsc->fns->print(child_rsc, child_text, options, print_data);
247  if (options & pe_print_html) {
248  status_print("</li>\n");
249  }
250  }
251  }
252 
253  if (options & pe_print_html) {
254  status_print("</ul>\n");
255  }
256  free(child_text);
257 }
258 
259 PCMK__OUTPUT_ARGS("group", "uint32_t", "pe_resource_t *", "GList *", "GList *")
260 int
261 pe__group_xml(pcmk__output_t *out, va_list args)
262 {
263  uint32_t show_opts = va_arg(args, uint32_t);
264  pe_resource_t *rsc = va_arg(args, pe_resource_t *);
265  GList *only_node = va_arg(args, GList *);
266  GList *only_rsc = va_arg(args, GList *);
267 
268  GList *gIter = rsc->children;
269  char *count = pcmk__itoa(g_list_length(gIter));
270 
271  int rc = pcmk_rc_no_output;
272 
273  gboolean parent_passes = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
274  (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
275 
276  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
277  free(count);
278  return rc;
279  }
280 
281  for (; gIter != NULL; gIter = gIter->next) {
282  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
283 
284  if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
285  continue;
286  }
287 
288  if (rc == pcmk_rc_no_output) {
289  rc = pe__name_and_nvpairs_xml(out, true, "group", 4
290  , "id", rsc->id
291  , "number_resources", count
292  , "managed", pe__rsc_bool_str(rsc, pe_rsc_managed)
293  , "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)));
294  free(count);
295  CRM_ASSERT(rc == pcmk_rc_ok);
296  }
297 
298  out->message(out, crm_map_element_name(child_rsc->xml), show_opts, child_rsc,
299  only_node, only_rsc);
300  }
301 
302  if (rc == pcmk_rc_ok) {
304  }
305 
306  return rc;
307 }
308 
309 PCMK__OUTPUT_ARGS("group", "uint32_t", "pe_resource_t *", "GList *", "GList *")
310 int
312 {
313  uint32_t show_opts = va_arg(args, uint32_t);
314  pe_resource_t *rsc = va_arg(args, pe_resource_t *);
315  GList *only_node = va_arg(args, GList *);
316  GList *only_rsc = va_arg(args, GList *);
317 
318  int rc = pcmk_rc_no_output;
319 
320  gboolean parent_passes = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
321  (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
322 
323  gboolean active = rsc->fns->active(rsc, TRUE);
324  gboolean partially_active = rsc->fns->active(rsc, FALSE);
325 
326  if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
327  return rc;
328  }
329 
330  if (pcmk_is_set(show_opts, pcmk_show_brief)) {
331  GList *rscs = pe__filter_rsc_list(rsc->children, only_rsc);
332 
333  if (rscs != NULL) {
334  group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
336  pe__rscs_brief_output(out, rscs, show_opts | pcmk_show_inactive_rscs);
337 
338  rc = pcmk_rc_ok;
339  g_list_free(rscs);
340  }
341 
342  } else {
343  for (GList *gIter = rsc->children; gIter; gIter = gIter->next) {
344  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
345 
346  if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
347  continue;
348  }
349 
350  group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
352  out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
353  child_rsc, only_node, only_rsc);
354  }
355  }
356 
357  PCMK__OUTPUT_LIST_FOOTER(out, rc);
358 
359  return rc;
360 }
361 
362 void
364 {
365  CRM_CHECK(rsc != NULL, return);
366 
367  pe_rsc_trace(rsc, "Freeing %s", rsc->id);
368 
369  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
370  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
371 
372  CRM_ASSERT(child_rsc);
373  pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
374  child_rsc->fns->free(child_rsc);
375  }
376 
377  pe_rsc_trace(rsc, "Freeing child list");
378  g_list_free(rsc->children);
379 
380  common_free(rsc);
381 }
382 
383 enum rsc_role_e
384 group_resource_state(const pe_resource_t * rsc, gboolean current)
385 {
386  enum rsc_role_e group_role = RSC_ROLE_UNKNOWN;
387  GList *gIter = rsc->children;
388 
389  for (; gIter != NULL; gIter = gIter->next) {
390  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
391  enum rsc_role_e role = child_rsc->fns->state(child_rsc, current);
392 
393  if (role > group_role) {
394  group_role = role;
395  }
396  }
397 
398  pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(group_role));
399  return group_role;
400 }
401 
402 gboolean
403 pe__group_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
404 {
405  gboolean passes = FALSE;
406 
407  if (check_parent && pcmk__str_in_list(rsc_printable_id(uber_parent(rsc)), only_rsc, pcmk__str_star_matches)) {
408  passes = TRUE;
409  } else if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
410  passes = TRUE;
411  } else if (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
412  passes = TRUE;
413  } else {
414  for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
415  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
416 
417  if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
418  passes = TRUE;
419  break;
420  }
421  }
422  }
423 
424  return !passes;
425 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
void pcmk__add_separated_word(char **list, size_t *len, const char *word, const char *separator)
Definition: strings.c:703
int pe__group_xml(pcmk__output_t *out, va_list args)
Definition: group.c:261
char data[0]
Definition: cpg.c:55
Control output from tools.
#define pcmk__config_warn(fmt...)
GList * children
Definition: pe_types.h:391
xmlNode * xml
Definition: pe_types.h:338
#define XML_RSC_ATTR_INCARNATION
Definition: msg_xml.h:231
GHashTable * meta
Definition: pe_types.h:387
resource_object_functions_t * fns
Definition: pe_types.h:347
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:1190
GList * pe__filter_rsc_list(GList *rscs, GList *filter)
Definition: utils.c:2485
const char * role2text(enum rsc_role_e role)
Definition: common.c:454
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:99
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:913
#define XML_ATTR_ID
Definition: msg_xml.h:135
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
#define XML_CIB_TAG_RESOURCE
Definition: msg_xml.h:220
#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:496
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:122
void common_free(pe_resource_t *rsc)
Definition: complex.c:927
int pe__group_default(pcmk__output_t *out, va_list args)
Definition: group.c:311
unsigned long long flags
Definition: pe_types.h:362
#define XML_RSC_ATTR_ORDERED
Definition: msg_xml.h:229
pe_working_set_t * data_set
gboolean pcmk__str_in_list(const gchar *s, GList *lst, uint32_t flags)
Definition: strings.c:886
void(* print)(pe_resource_t *, const char *, long, void *)
Definition: pe_types.h:52
void(* free)(pe_resource_t *)
Definition: pe_types.h:56
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:512
gboolean pe__group_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
Definition: group.c:403
#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:211
#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:346
#define CRM_ASSERT(expr)
Definition: results.h:42
const char * rsc_printable_id(pe_resource_t *rsc)
Definition: utils.c:2012
#define status_print(fmt, args...)
enum rsc_role_e group_resource_state(const pe_resource_t *rsc, gboolean current)
Definition: group.c:384
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
gboolean common_unpack(xmlNode *xml_obj, pe_resource_t **rsc, pe_resource_t *parent, pe_working_set_t *data_set)
Definition: complex.c:493
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:22
bool pe__resource_is_disabled(pe_resource_t *rsc)
Definition: utils.c:2425
#define pe_err(fmt...)
Definition: internal.h:24
void group_free(pe_resource_t *rsc)
Definition: group.c:363
#define pe_rsc_managed
Definition: pe_types.h:257
gboolean group_active(pe_resource_t *rsc, gboolean all)
Definition: group.c:166
gboolean(* active)(pe_resource_t *, gboolean)
Definition: pe_types.h:53
char * id
Definition: pe_types.h:336