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