Line data Source code
1 : /*
2 : * Copyright 2019-2024 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 General Public License version 2
7 : * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 : */
9 :
10 : #include <crm_internal.h>
11 : #include <crm/common/output.h>
12 : #include <crm/common/results.h>
13 : #include <crm/common/xml.h>
14 : #include <crm/stonith-ng.h>
15 : #include <crm/fencing/internal.h> // stonith__*
16 : #include <crm/pengine/internal.h>
17 : #include <libxml/tree.h>
18 : #include <pacemaker-internal.h>
19 :
20 : #include <inttypes.h>
21 : #include <stdint.h>
22 :
23 : static char *
24 0 : colocations_header(pcmk_resource_t *rsc, pcmk__colocation_t *cons,
25 : bool dependents) {
26 0 : char *retval = NULL;
27 :
28 0 : if (cons->primary_role > pcmk_role_started) {
29 0 : retval = crm_strdup_printf("%s (score=%s, %s role=%s, id=%s)",
30 : rsc->id, pcmk_readable_score(cons->score),
31 : (dependents? "needs" : "with"),
32 0 : pcmk_role_text(cons->primary_role),
33 : cons->id);
34 : } else {
35 0 : retval = crm_strdup_printf("%s (score=%s, id=%s)",
36 : rsc->id, pcmk_readable_score(cons->score),
37 : cons->id);
38 : }
39 0 : return retval;
40 : }
41 :
42 : static void
43 0 : colocations_xml_node(pcmk__output_t *out, pcmk_resource_t *rsc,
44 : pcmk__colocation_t *cons) {
45 0 : xmlNodePtr node = NULL;
46 :
47 0 : node = pcmk__output_create_xml_node(out, PCMK_XE_RSC_COLOCATION,
48 : PCMK_XA_ID, cons->id,
49 0 : PCMK_XA_RSC, cons->dependent->id,
50 0 : PCMK_XA_WITH_RSC, cons->primary->id,
51 : PCMK_XA_SCORE,
52 : pcmk_readable_score(cons->score),
53 : NULL);
54 :
55 0 : if (cons->node_attribute) {
56 0 : xmlSetProp(node, (pcmkXmlStr) PCMK_XA_NODE_ATTRIBUTE,
57 0 : (pcmkXmlStr) cons->node_attribute);
58 : }
59 :
60 0 : if (cons->dependent_role != pcmk_role_unknown) {
61 0 : xmlSetProp(node, (pcmkXmlStr) PCMK_XA_RSC_ROLE,
62 0 : (pcmkXmlStr) pcmk_role_text(cons->dependent_role));
63 : }
64 :
65 0 : if (cons->primary_role != pcmk_role_unknown) {
66 0 : xmlSetProp(node, (pcmkXmlStr) PCMK_XA_WITH_RSC_ROLE,
67 0 : (pcmkXmlStr) pcmk_role_text(cons->primary_role));
68 : }
69 0 : }
70 :
71 : static int
72 0 : do_locations_list_xml(pcmk__output_t *out, pcmk_resource_t *rsc,
73 : bool add_header)
74 : {
75 0 : GList *lpc = NULL;
76 0 : GList *list = rsc->rsc_location;
77 0 : int rc = pcmk_rc_no_output;
78 :
79 0 : for (lpc = list; lpc != NULL; lpc = lpc->next) {
80 0 : pcmk__location_t *cons = lpc->data;
81 :
82 0 : GList *lpc2 = NULL;
83 :
84 0 : for (lpc2 = cons->nodes; lpc2 != NULL; lpc2 = lpc2->next) {
85 0 : pcmk_node_t *node = (pcmk_node_t *) lpc2->data;
86 :
87 0 : if (add_header) {
88 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "locations");
89 : }
90 :
91 0 : pcmk__output_create_xml_node(out, PCMK_XE_RSC_LOCATION,
92 0 : PCMK_XA_NODE, node->details->uname,
93 : PCMK_XA_RSC, rsc->id,
94 : PCMK_XA_ID, cons->id,
95 : PCMK_XA_SCORE,
96 : pcmk_readable_score(node->weight),
97 : NULL);
98 : }
99 : }
100 :
101 0 : if (add_header) {
102 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
103 : }
104 :
105 0 : return rc;
106 : }
107 :
108 : PCMK__OUTPUT_ARGS("rsc-action-item", "const char *", "pcmk_resource_t *",
109 : "pcmk_node_t *", "pcmk_node_t *", "pcmk_action_t *",
110 : "pcmk_action_t *")
111 : static int
112 0 : rsc_action_item(pcmk__output_t *out, va_list args)
113 : {
114 0 : const char *change = va_arg(args, const char *);
115 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
116 0 : pcmk_node_t *origin = va_arg(args, pcmk_node_t *);
117 0 : pcmk_node_t *destination = va_arg(args, pcmk_node_t *);
118 0 : pcmk_action_t *action = va_arg(args, pcmk_action_t *);
119 0 : pcmk_action_t *source = va_arg(args, pcmk_action_t *);
120 :
121 0 : int len = 0;
122 0 : char *reason = NULL;
123 0 : char *details = NULL;
124 0 : bool same_host = false;
125 0 : bool same_role = false;
126 0 : bool need_role = false;
127 :
128 : static int rsc_width = 5;
129 : static int detail_width = 5;
130 :
131 0 : CRM_ASSERT(action);
132 0 : CRM_ASSERT(destination != NULL || origin != NULL);
133 :
134 0 : if (source == NULL) {
135 0 : source = action;
136 : }
137 :
138 0 : len = strlen(rsc->id);
139 0 : if (len > rsc_width) {
140 0 : rsc_width = len + 2;
141 : }
142 :
143 0 : if ((rsc->role > pcmk_role_started)
144 0 : || (rsc->next_role > pcmk_role_unpromoted)) {
145 0 : need_role = true;
146 : }
147 :
148 0 : if (pcmk__same_node(origin, destination)) {
149 0 : same_host = true;
150 : }
151 :
152 0 : if (rsc->role == rsc->next_role) {
153 0 : same_role = true;
154 : }
155 :
156 0 : if (need_role && (origin == NULL)) {
157 : /* Starting and promoting a promotable clone instance */
158 0 : details = crm_strdup_printf("%s -> %s %s", pcmk_role_text(rsc->role),
159 : pcmk_role_text(rsc->next_role),
160 : pcmk__node_name(destination));
161 :
162 0 : } else if (origin == NULL) {
163 : /* Starting a resource */
164 0 : details = crm_strdup_printf("%s", pcmk__node_name(destination));
165 :
166 0 : } else if (need_role && (destination == NULL)) {
167 : /* Stopping a promotable clone instance */
168 0 : details = crm_strdup_printf("%s %s", pcmk_role_text(rsc->role),
169 : pcmk__node_name(origin));
170 :
171 0 : } else if (destination == NULL) {
172 : /* Stopping a resource */
173 0 : details = crm_strdup_printf("%s", pcmk__node_name(origin));
174 :
175 0 : } else if (need_role && same_role && same_host) {
176 : /* Recovering, restarting or re-promoting a promotable clone instance */
177 0 : details = crm_strdup_printf("%s %s", pcmk_role_text(rsc->role),
178 : pcmk__node_name(origin));
179 :
180 0 : } else if (same_role && same_host) {
181 : /* Recovering or Restarting a normal resource */
182 0 : details = crm_strdup_printf("%s", pcmk__node_name(origin));
183 :
184 0 : } else if (need_role && same_role) {
185 : /* Moving a promotable clone instance */
186 0 : details = crm_strdup_printf("%s -> %s %s", pcmk__node_name(origin),
187 : pcmk__node_name(destination),
188 : pcmk_role_text(rsc->role));
189 :
190 0 : } else if (same_role) {
191 : /* Moving a normal resource */
192 0 : details = crm_strdup_printf("%s -> %s", pcmk__node_name(origin),
193 : pcmk__node_name(destination));
194 :
195 0 : } else if (same_host) {
196 : /* Promoting or demoting a promotable clone instance */
197 0 : details = crm_strdup_printf("%s -> %s %s", pcmk_role_text(rsc->role),
198 : pcmk_role_text(rsc->next_role),
199 : pcmk__node_name(origin));
200 :
201 : } else {
202 : /* Moving and promoting/demoting */
203 0 : details = crm_strdup_printf("%s %s -> %s %s",
204 : pcmk_role_text(rsc->role),
205 : pcmk__node_name(origin),
206 : pcmk_role_text(rsc->next_role),
207 : pcmk__node_name(destination));
208 : }
209 :
210 0 : len = strlen(details);
211 0 : if (len > detail_width) {
212 0 : detail_width = len;
213 : }
214 :
215 0 : if ((source->reason != NULL)
216 0 : && !pcmk_is_set(action->flags, pcmk_action_runnable)) {
217 0 : reason = crm_strdup_printf("due to %s (blocked)", source->reason);
218 :
219 0 : } else if (source->reason) {
220 0 : reason = crm_strdup_printf("due to %s", source->reason);
221 :
222 0 : } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
223 0 : reason = strdup("blocked");
224 :
225 : }
226 :
227 0 : out->list_item(out, NULL, "%-8s %-*s ( %*s )%s%s",
228 : change, rsc_width, rsc->id, detail_width, details,
229 : ((reason == NULL)? "" : " "), pcmk__s(reason, ""));
230 :
231 0 : free(details);
232 0 : free(reason);
233 0 : return pcmk_rc_ok;
234 : }
235 :
236 : PCMK__OUTPUT_ARGS("rsc-action-item", "const char *", "pcmk_resource_t *",
237 : "pcmk_node_t *", "pcmk_node_t *", "pcmk_action_t *",
238 : "pcmk_action_t *")
239 : static int
240 0 : rsc_action_item_xml(pcmk__output_t *out, va_list args)
241 : {
242 0 : const char *change = va_arg(args, const char *);
243 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
244 0 : pcmk_node_t *origin = va_arg(args, pcmk_node_t *);
245 0 : pcmk_node_t *destination = va_arg(args, pcmk_node_t *);
246 0 : pcmk_action_t *action = va_arg(args, pcmk_action_t *);
247 0 : pcmk_action_t *source = va_arg(args, pcmk_action_t *);
248 :
249 0 : char *change_str = NULL;
250 :
251 0 : bool same_host = false;
252 0 : bool same_role = false;
253 0 : bool need_role = false;
254 0 : xmlNode *xml = NULL;
255 :
256 0 : CRM_ASSERT(action);
257 0 : CRM_ASSERT(destination != NULL || origin != NULL);
258 :
259 0 : if (source == NULL) {
260 0 : source = action;
261 : }
262 :
263 0 : if ((rsc->role > pcmk_role_started)
264 0 : || (rsc->next_role > pcmk_role_unpromoted)) {
265 0 : need_role = true;
266 : }
267 :
268 0 : if (pcmk__same_node(origin, destination)) {
269 0 : same_host = true;
270 : }
271 :
272 0 : if (rsc->role == rsc->next_role) {
273 0 : same_role = true;
274 : }
275 :
276 0 : change_str = g_ascii_strdown(change, -1);
277 0 : xml = pcmk__output_create_xml_node(out, PCMK_XE_RSC_ACTION,
278 : PCMK_XA_ACTION, change_str,
279 : PCMK_XA_RESOURCE, rsc->id,
280 : NULL);
281 0 : g_free(change_str);
282 :
283 0 : if (need_role && (origin == NULL)) {
284 : /* Starting and promoting a promotable clone instance */
285 0 : pcmk__xe_set_props(xml,
286 : PCMK_XA_ROLE, pcmk_role_text(rsc->role),
287 : PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role),
288 0 : PCMK_XA_DEST, destination->details->uname,
289 : NULL);
290 :
291 0 : } else if (origin == NULL) {
292 : /* Starting a resource */
293 0 : crm_xml_add(xml, PCMK_XA_NODE, destination->details->uname);
294 :
295 0 : } else if (need_role && (destination == NULL)) {
296 : /* Stopping a promotable clone instance */
297 0 : pcmk__xe_set_props(xml,
298 : PCMK_XA_ROLE, pcmk_role_text(rsc->role),
299 0 : PCMK_XA_NODE, origin->details->uname,
300 : NULL);
301 :
302 0 : } else if (destination == NULL) {
303 : /* Stopping a resource */
304 0 : crm_xml_add(xml, PCMK_XA_NODE, origin->details->uname);
305 :
306 0 : } else if (need_role && same_role && same_host) {
307 : /* Recovering, restarting or re-promoting a promotable clone instance */
308 0 : pcmk__xe_set_props(xml,
309 : PCMK_XA_ROLE, pcmk_role_text(rsc->role),
310 0 : PCMK_XA_SOURCE, origin->details->uname,
311 : NULL);
312 :
313 0 : } else if (same_role && same_host) {
314 : /* Recovering or Restarting a normal resource */
315 0 : crm_xml_add(xml, PCMK_XA_SOURCE, origin->details->uname);
316 :
317 0 : } else if (need_role && same_role) {
318 : /* Moving a promotable clone instance */
319 0 : pcmk__xe_set_props(xml,
320 0 : PCMK_XA_SOURCE, origin->details->uname,
321 0 : PCMK_XA_DEST, destination->details->uname,
322 : PCMK_XA_ROLE, pcmk_role_text(rsc->role),
323 : NULL);
324 :
325 0 : } else if (same_role) {
326 : /* Moving a normal resource */
327 0 : pcmk__xe_set_props(xml,
328 0 : PCMK_XA_SOURCE, origin->details->uname,
329 0 : PCMK_XA_DEST, destination->details->uname,
330 : NULL);
331 :
332 0 : } else if (same_host) {
333 : /* Promoting or demoting a promotable clone instance */
334 0 : pcmk__xe_set_props(xml,
335 : PCMK_XA_ROLE, pcmk_role_text(rsc->role),
336 : PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role),
337 0 : PCMK_XA_SOURCE, origin->details->uname,
338 : NULL);
339 :
340 : } else {
341 : /* Moving and promoting/demoting */
342 0 : pcmk__xe_set_props(xml,
343 : PCMK_XA_ROLE, pcmk_role_text(rsc->role),
344 0 : PCMK_XA_SOURCE, origin->details->uname,
345 : PCMK_XA_NEXT_ROLE, pcmk_role_text(rsc->next_role),
346 0 : PCMK_XA_DEST, destination->details->uname,
347 : NULL);
348 : }
349 :
350 0 : if ((source->reason != NULL)
351 0 : && !pcmk_is_set(action->flags, pcmk_action_runnable)) {
352 0 : pcmk__xe_set_props(xml,
353 : PCMK_XA_REASON, source->reason,
354 : PCMK_XA_BLOCKED, PCMK_VALUE_TRUE,
355 : NULL);
356 :
357 0 : } else if (source->reason != NULL) {
358 0 : crm_xml_add(xml, PCMK_XA_REASON, source->reason);
359 :
360 0 : } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
361 0 : pcmk__xe_set_bool_attr(xml, PCMK_XA_BLOCKED, true);
362 :
363 : }
364 :
365 0 : return pcmk_rc_ok;
366 : }
367 :
368 : PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pcmk_resource_t *", "bool")
369 : static int
370 0 : rsc_is_colocated_with_list(pcmk__output_t *out, va_list args) {
371 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
372 0 : bool recursive = va_arg(args, int);
373 :
374 0 : int rc = pcmk_rc_no_output;
375 :
376 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_detect_loop)) {
377 0 : return rc;
378 : }
379 :
380 : /* We're listing constraints explicitly involving rsc, so use rsc->rsc_cons
381 : * directly rather than rsc->cmds->this_with_colocations().
382 : */
383 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
384 0 : for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
385 0 : pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
386 0 : char *hdr = NULL;
387 :
388 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc,
389 : "Resources %s is colocated with", rsc->id);
390 :
391 0 : if (pcmk_is_set(cons->primary->flags, pcmk_rsc_detect_loop)) {
392 0 : out->list_item(out, NULL, "%s (id=%s - loop)",
393 0 : cons->primary->id, cons->id);
394 0 : continue;
395 : }
396 :
397 0 : hdr = colocations_header(cons->primary, cons, false);
398 0 : out->list_item(out, NULL, "%s", hdr);
399 0 : free(hdr);
400 :
401 : // Empty list header for indentation of information about this resource
402 0 : out->begin_list(out, NULL, NULL, NULL);
403 :
404 0 : out->message(out, "locations-list", cons->primary);
405 0 : if (recursive) {
406 0 : out->message(out, "rsc-is-colocated-with-list",
407 : cons->primary, recursive);
408 : }
409 :
410 0 : out->end_list(out);
411 : }
412 :
413 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
414 0 : return rc;
415 : }
416 :
417 : PCMK__OUTPUT_ARGS("rsc-is-colocated-with-list", "pcmk_resource_t *", "bool")
418 : static int
419 0 : rsc_is_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
420 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
421 0 : bool recursive = va_arg(args, int);
422 :
423 0 : int rc = pcmk_rc_no_output;
424 :
425 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_detect_loop)) {
426 0 : return rc;
427 : }
428 :
429 : /* We're listing constraints explicitly involving rsc, so use rsc->rsc_cons
430 : * directly rather than rsc->cmds->this_with_colocations().
431 : */
432 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
433 0 : for (GList *lpc = rsc->rsc_cons; lpc != NULL; lpc = lpc->next) {
434 0 : pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
435 :
436 0 : if (pcmk_is_set(cons->primary->flags, pcmk_rsc_detect_loop)) {
437 0 : colocations_xml_node(out, cons->primary, cons);
438 0 : continue;
439 : }
440 :
441 0 : colocations_xml_node(out, cons->primary, cons);
442 0 : do_locations_list_xml(out, cons->primary, false);
443 :
444 0 : if (recursive) {
445 0 : out->message(out, "rsc-is-colocated-with-list",
446 : cons->primary, recursive);
447 : }
448 : }
449 :
450 0 : return rc;
451 : }
452 :
453 : PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pcmk_resource_t *", "bool")
454 : static int
455 0 : rscs_colocated_with_list(pcmk__output_t *out, va_list args) {
456 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
457 0 : bool recursive = va_arg(args, int);
458 :
459 0 : int rc = pcmk_rc_no_output;
460 :
461 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_detect_loop)) {
462 0 : return rc;
463 : }
464 :
465 : /* We're listing constraints explicitly involving rsc, so use
466 : * rsc->rsc_cons_lhs directly rather than
467 : * rsc->cmds->with_this_colocations().
468 : */
469 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
470 0 : for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
471 0 : pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
472 0 : char *hdr = NULL;
473 :
474 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Resources colocated with %s",
475 : rsc->id);
476 :
477 0 : if (pcmk_is_set(cons->dependent->flags, pcmk_rsc_detect_loop)) {
478 0 : out->list_item(out, NULL, "%s (id=%s - loop)",
479 0 : cons->dependent->id, cons->id);
480 0 : continue;
481 : }
482 :
483 0 : hdr = colocations_header(cons->dependent, cons, true);
484 0 : out->list_item(out, NULL, "%s", hdr);
485 0 : free(hdr);
486 :
487 : // Empty list header for indentation of information about this resource
488 0 : out->begin_list(out, NULL, NULL, NULL);
489 :
490 0 : out->message(out, "locations-list", cons->dependent);
491 0 : if (recursive) {
492 0 : out->message(out, "rscs-colocated-with-list",
493 : cons->dependent, recursive);
494 : }
495 :
496 0 : out->end_list(out);
497 : }
498 :
499 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
500 0 : return rc;
501 : }
502 :
503 : PCMK__OUTPUT_ARGS("rscs-colocated-with-list", "pcmk_resource_t *", "bool")
504 : static int
505 0 : rscs_colocated_with_list_xml(pcmk__output_t *out, va_list args) {
506 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
507 0 : bool recursive = va_arg(args, int);
508 :
509 0 : int rc = pcmk_rc_no_output;
510 :
511 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_detect_loop)) {
512 0 : return rc;
513 : }
514 :
515 : /* We're listing constraints explicitly involving rsc, so use
516 : * rsc->rsc_cons_lhs directly rather than
517 : * rsc->cmds->with_this_colocations().
518 : */
519 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
520 0 : for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
521 0 : pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
522 :
523 0 : if (pcmk_is_set(cons->dependent->flags, pcmk_rsc_detect_loop)) {
524 0 : colocations_xml_node(out, cons->dependent, cons);
525 0 : continue;
526 : }
527 :
528 0 : colocations_xml_node(out, cons->dependent, cons);
529 0 : do_locations_list_xml(out, cons->dependent, false);
530 :
531 0 : if (recursive) {
532 0 : out->message(out, "rscs-colocated-with-list",
533 : cons->dependent, recursive);
534 : }
535 : }
536 :
537 0 : return rc;
538 : }
539 :
540 : PCMK__OUTPUT_ARGS("locations-list", "pcmk_resource_t *")
541 : static int
542 0 : locations_list(pcmk__output_t *out, va_list args) {
543 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
544 :
545 0 : GList *lpc = NULL;
546 0 : GList *list = rsc->rsc_location;
547 0 : int rc = pcmk_rc_no_output;
548 :
549 0 : for (lpc = list; lpc != NULL; lpc = lpc->next) {
550 0 : pcmk__location_t *cons = lpc->data;
551 :
552 0 : GList *lpc2 = NULL;
553 :
554 0 : for (lpc2 = cons->nodes; lpc2 != NULL; lpc2 = lpc2->next) {
555 0 : pcmk_node_t *node = (pcmk_node_t *) lpc2->data;
556 :
557 0 : PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Locations");
558 0 : out->list_item(out, NULL, "Node %s (score=%s, id=%s, rsc=%s)",
559 : pcmk__node_name(node),
560 : pcmk_readable_score(node->weight), cons->id,
561 : rsc->id);
562 : }
563 : }
564 :
565 0 : PCMK__OUTPUT_LIST_FOOTER(out, rc);
566 0 : return rc;
567 : }
568 :
569 : PCMK__OUTPUT_ARGS("locations-list", "pcmk_resource_t *")
570 : static int
571 0 : locations_list_xml(pcmk__output_t *out, va_list args) {
572 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
573 0 : return do_locations_list_xml(out, rsc, true);
574 : }
575 :
576 : PCMK__OUTPUT_ARGS("locations-and-colocations", "pcmk_resource_t *",
577 : "bool", "bool")
578 : static int
579 0 : locations_and_colocations(pcmk__output_t *out, va_list args)
580 : {
581 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
582 0 : bool recursive = va_arg(args, int);
583 0 : bool force = va_arg(args, int);
584 :
585 0 : pcmk__unpack_constraints(rsc->cluster);
586 :
587 : // Constraints apply to group/clone, not member/instance
588 0 : if (!force) {
589 0 : rsc = uber_parent(rsc);
590 : }
591 :
592 0 : out->message(out, "locations-list", rsc);
593 :
594 0 : pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
595 0 : out->message(out, "rscs-colocated-with-list", rsc, recursive);
596 :
597 0 : pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
598 0 : out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
599 0 : return pcmk_rc_ok;
600 : }
601 :
602 : PCMK__OUTPUT_ARGS("locations-and-colocations", "pcmk_resource_t *",
603 : "bool", "bool")
604 : static int
605 0 : locations_and_colocations_xml(pcmk__output_t *out, va_list args)
606 : {
607 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
608 0 : bool recursive = va_arg(args, int);
609 0 : bool force = va_arg(args, int);
610 :
611 0 : pcmk__unpack_constraints(rsc->cluster);
612 :
613 : // Constraints apply to group/clone, not member/instance
614 0 : if (!force) {
615 0 : rsc = uber_parent(rsc);
616 : }
617 :
618 0 : pcmk__output_xml_create_parent(out, PCMK_XE_CONSTRAINTS, NULL);
619 0 : do_locations_list_xml(out, rsc, false);
620 :
621 0 : pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
622 0 : out->message(out, "rscs-colocated-with-list", rsc, recursive);
623 :
624 0 : pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
625 0 : out->message(out, "rsc-is-colocated-with-list", rsc, recursive);
626 :
627 0 : pcmk__output_xml_pop_parent(out);
628 0 : return pcmk_rc_ok;
629 : }
630 :
631 : PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *",
632 : "const char *")
633 : static int
634 0 : health(pcmk__output_t *out, va_list args)
635 : {
636 0 : const char *sys_from G_GNUC_UNUSED = va_arg(args, const char *);
637 0 : const char *host_from = va_arg(args, const char *);
638 0 : const char *fsa_state = va_arg(args, const char *);
639 0 : const char *result = va_arg(args, const char *);
640 :
641 0 : return out->info(out, "Controller on %s in state %s: %s",
642 : pcmk__s(host_from, "unknown node"),
643 : pcmk__s(fsa_state, "unknown"),
644 : pcmk__s(result, "unknown result"));
645 : }
646 :
647 : PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *",
648 : "const char *")
649 : static int
650 0 : health_text(pcmk__output_t *out, va_list args)
651 : {
652 0 : if (!out->is_quiet(out)) {
653 0 : return health(out, args);
654 : } else {
655 0 : const char *sys_from G_GNUC_UNUSED = va_arg(args, const char *);
656 0 : const char *host_from G_GNUC_UNUSED = va_arg(args, const char *);
657 0 : const char *fsa_state = va_arg(args, const char *);
658 0 : const char *result G_GNUC_UNUSED = va_arg(args, const char *);
659 :
660 0 : if (fsa_state != NULL) {
661 0 : pcmk__formatted_printf(out, "%s\n", fsa_state);
662 0 : return pcmk_rc_ok;
663 : }
664 : }
665 :
666 0 : return pcmk_rc_no_output;
667 : }
668 :
669 : PCMK__OUTPUT_ARGS("health", "const char *", "const char *", "const char *",
670 : "const char *")
671 : static int
672 0 : health_xml(pcmk__output_t *out, va_list args)
673 : {
674 0 : const char *sys_from = va_arg(args, const char *);
675 0 : const char *host_from = va_arg(args, const char *);
676 0 : const char *fsa_state = va_arg(args, const char *);
677 0 : const char *result = va_arg(args, const char *);
678 :
679 0 : pcmk__output_create_xml_node(out, pcmk__s(sys_from, ""),
680 : PCMK_XA_NODE_NAME, pcmk__s(host_from, ""),
681 : PCMK_XA_STATE, pcmk__s(fsa_state, ""),
682 : PCMK_XA_RESULT, pcmk__s(result, ""),
683 : NULL);
684 0 : return pcmk_rc_ok;
685 : }
686 :
687 : PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *",
688 : "enum pcmk_pacemakerd_state", "const char *", "time_t")
689 : static int
690 0 : pacemakerd_health(pcmk__output_t *out, va_list args)
691 : {
692 0 : const char *sys_from = va_arg(args, const char *);
693 0 : enum pcmk_pacemakerd_state state =
694 : (enum pcmk_pacemakerd_state) va_arg(args, int);
695 0 : const char *state_s = va_arg(args, const char *);
696 0 : time_t last_updated = va_arg(args, time_t);
697 :
698 0 : char *last_updated_s = NULL;
699 0 : int rc = pcmk_rc_ok;
700 :
701 0 : if (sys_from == NULL) {
702 0 : if (state == pcmk_pacemakerd_state_remote) {
703 0 : sys_from = "pacemaker-remoted";
704 : } else {
705 0 : sys_from = CRM_SYSTEM_MCP;
706 : }
707 : }
708 :
709 0 : if (state_s == NULL) {
710 0 : state_s = pcmk__pcmkd_state_enum2friendly(state);
711 : }
712 :
713 0 : if (last_updated != 0) {
714 0 : last_updated_s = pcmk__epoch2str(&last_updated,
715 : crm_time_log_date
716 : |crm_time_log_timeofday
717 : |crm_time_log_with_timezone);
718 : }
719 :
720 0 : rc = out->info(out, "Status of %s: '%s' (last updated %s)",
721 : sys_from, state_s,
722 : pcmk__s(last_updated_s, "at unknown time"));
723 :
724 0 : free(last_updated_s);
725 0 : return rc;
726 : }
727 :
728 : PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *",
729 : "enum pcmk_pacemakerd_state", "const char *", "time_t")
730 : static int
731 0 : pacemakerd_health_html(pcmk__output_t *out, va_list args)
732 : {
733 0 : const char *sys_from = va_arg(args, const char *);
734 0 : enum pcmk_pacemakerd_state state =
735 : (enum pcmk_pacemakerd_state) va_arg(args, int);
736 0 : const char *state_s = va_arg(args, const char *);
737 0 : time_t last_updated = va_arg(args, time_t);
738 :
739 0 : char *last_updated_s = NULL;
740 0 : char *msg = NULL;
741 :
742 0 : if (sys_from == NULL) {
743 0 : if (state == pcmk_pacemakerd_state_remote) {
744 0 : sys_from = "pacemaker-remoted";
745 : } else {
746 0 : sys_from = CRM_SYSTEM_MCP;
747 : }
748 : }
749 :
750 0 : if (state_s == NULL) {
751 0 : state_s = pcmk__pcmkd_state_enum2friendly(state);
752 : }
753 :
754 0 : if (last_updated != 0) {
755 0 : last_updated_s = pcmk__epoch2str(&last_updated,
756 : crm_time_log_date
757 : |crm_time_log_timeofday
758 : |crm_time_log_with_timezone);
759 : }
760 :
761 0 : msg = crm_strdup_printf("Status of %s: '%s' (last updated %s)",
762 : sys_from, state_s,
763 : pcmk__s(last_updated_s, "at unknown time"));
764 0 : pcmk__output_create_html_node(out, "li", NULL, NULL, msg);
765 :
766 0 : free(msg);
767 0 : free(last_updated_s);
768 0 : return pcmk_rc_ok;
769 : }
770 :
771 : PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *",
772 : "enum pcmk_pacemakerd_state", "const char *", "time_t")
773 : static int
774 0 : pacemakerd_health_text(pcmk__output_t *out, va_list args)
775 : {
776 0 : if (!out->is_quiet(out)) {
777 0 : return pacemakerd_health(out, args);
778 : } else {
779 0 : const char *sys_from G_GNUC_UNUSED = va_arg(args, const char *);
780 0 : enum pcmk_pacemakerd_state state =
781 : (enum pcmk_pacemakerd_state) va_arg(args, int);
782 0 : const char *state_s = va_arg(args, const char *);
783 0 : time_t last_updated G_GNUC_UNUSED = va_arg(args, time_t);
784 :
785 0 : if (state_s == NULL) {
786 0 : state_s = pcmk_pacemakerd_api_daemon_state_enum2text(state);
787 : }
788 0 : pcmk__formatted_printf(out, "%s\n", state_s);
789 0 : return pcmk_rc_ok;
790 : }
791 : }
792 :
793 : PCMK__OUTPUT_ARGS("pacemakerd-health", "const char *",
794 : "enum pcmk_pacemakerd_state", "const char *", "time_t")
795 : static int
796 0 : pacemakerd_health_xml(pcmk__output_t *out, va_list args)
797 : {
798 0 : const char *sys_from = va_arg(args, const char *);
799 0 : enum pcmk_pacemakerd_state state =
800 : (enum pcmk_pacemakerd_state) va_arg(args, int);
801 0 : const char *state_s = va_arg(args, const char *);
802 0 : time_t last_updated = va_arg(args, time_t);
803 :
804 0 : char *last_updated_s = NULL;
805 :
806 0 : if (sys_from == NULL) {
807 0 : if (state == pcmk_pacemakerd_state_remote) {
808 0 : sys_from = "pacemaker-remoted";
809 : } else {
810 0 : sys_from = CRM_SYSTEM_MCP;
811 : }
812 : }
813 :
814 0 : if (state_s == NULL) {
815 0 : state_s = pcmk_pacemakerd_api_daemon_state_enum2text(state);
816 : }
817 :
818 0 : if (last_updated != 0) {
819 0 : last_updated_s = pcmk__epoch2str(&last_updated,
820 : crm_time_log_date
821 : |crm_time_log_timeofday
822 : |crm_time_log_with_timezone);
823 : }
824 :
825 0 : pcmk__output_create_xml_node(out, PCMK_XE_PACEMAKERD,
826 : PCMK_XA_SYS_FROM, sys_from,
827 : PCMK_XA_STATE, state_s,
828 : PCMK_XA_LAST_UPDATED, last_updated_s,
829 : NULL);
830 0 : free(last_updated_s);
831 0 : return pcmk_rc_ok;
832 : }
833 :
834 : PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
835 : static int
836 0 : profile_default(pcmk__output_t *out, va_list args) {
837 0 : const char *xml_file = va_arg(args, const char *);
838 0 : clock_t start = va_arg(args, clock_t);
839 0 : clock_t end = va_arg(args, clock_t);
840 :
841 0 : out->list_item(out, NULL, "Testing %s ... %.2f secs", xml_file,
842 0 : (end - start) / (float) CLOCKS_PER_SEC);
843 :
844 0 : return pcmk_rc_ok;
845 : }
846 :
847 : PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
848 : static int
849 0 : profile_xml(pcmk__output_t *out, va_list args) {
850 0 : const char *xml_file = va_arg(args, const char *);
851 0 : clock_t start = va_arg(args, clock_t);
852 0 : clock_t end = va_arg(args, clock_t);
853 :
854 0 : char *duration = pcmk__ftoa((end - start) / (float) CLOCKS_PER_SEC);
855 :
856 0 : pcmk__output_create_xml_node(out, PCMK_XE_TIMING,
857 : PCMK_XA_FILE, xml_file,
858 : PCMK_XA_DURATION, duration,
859 : NULL);
860 :
861 0 : free(duration);
862 0 : return pcmk_rc_ok;
863 : }
864 :
865 : PCMK__OUTPUT_ARGS("dc", "const char *")
866 : static int
867 0 : dc(pcmk__output_t *out, va_list args)
868 : {
869 0 : const char *dc = va_arg(args, const char *);
870 :
871 0 : return out->info(out, "Designated Controller is: %s",
872 : pcmk__s(dc, "not yet elected"));
873 : }
874 :
875 : PCMK__OUTPUT_ARGS("dc", "const char *")
876 : static int
877 0 : dc_text(pcmk__output_t *out, va_list args)
878 : {
879 0 : if (!out->is_quiet(out)) {
880 0 : return dc(out, args);
881 : } else {
882 0 : const char *dc = va_arg(args, const char *);
883 :
884 0 : if (dc != NULL) {
885 0 : pcmk__formatted_printf(out, "%s\n", pcmk__s(dc, ""));
886 0 : return pcmk_rc_ok;
887 : }
888 : }
889 :
890 0 : return pcmk_rc_no_output;
891 : }
892 :
893 : PCMK__OUTPUT_ARGS("dc", "const char *")
894 : static int
895 0 : dc_xml(pcmk__output_t *out, va_list args)
896 : {
897 0 : const char *dc = va_arg(args, const char *);
898 :
899 0 : pcmk__output_create_xml_node(out, PCMK_XE_DC,
900 : PCMK_XA_NODE_NAME, pcmk__s(dc, ""),
901 : NULL);
902 0 : return pcmk_rc_ok;
903 : }
904 :
905 : PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *",
906 : "const char *", "bool")
907 : static int
908 0 : crmadmin_node(pcmk__output_t *out, va_list args)
909 : {
910 0 : const char *type = va_arg(args, const char *);
911 0 : const char *name = va_arg(args, const char *);
912 0 : const char *id = va_arg(args, const char *);
913 0 : bool bash_export = va_arg(args, int);
914 :
915 0 : if (bash_export) {
916 0 : return out->info(out, "export %s=%s",
917 : pcmk__s(name, "<null>"), pcmk__s(id, ""));
918 : } else {
919 0 : return out->info(out, "%s node: %s (%s)", type ? type : "cluster",
920 : pcmk__s(name, "<null>"), pcmk__s(id, "<null>"));
921 : }
922 : }
923 :
924 : PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *",
925 : "const char *", "bool")
926 : static int
927 0 : crmadmin_node_text(pcmk__output_t *out, va_list args)
928 : {
929 0 : if (!out->is_quiet(out)) {
930 0 : return crmadmin_node(out, args);
931 : } else {
932 0 : const char *type G_GNUC_UNUSED = va_arg(args, const char *);
933 0 : const char *name = va_arg(args, const char *);
934 0 : const char *id G_GNUC_UNUSED = va_arg(args, const char *);
935 0 : bool bash_export G_GNUC_UNUSED = va_arg(args, int);
936 :
937 0 : pcmk__formatted_printf(out, "%s\n", pcmk__s(name, "<null>"));
938 0 : return pcmk_rc_ok;
939 : }
940 : }
941 :
942 : PCMK__OUTPUT_ARGS("crmadmin-node", "const char *", "const char *",
943 : "const char *", "bool")
944 : static int
945 0 : crmadmin_node_xml(pcmk__output_t *out, va_list args)
946 : {
947 0 : const char *type = va_arg(args, const char *);
948 0 : const char *name = va_arg(args, const char *);
949 0 : const char *id = va_arg(args, const char *);
950 0 : bool bash_export G_GNUC_UNUSED = va_arg(args, int);
951 :
952 0 : pcmk__output_create_xml_node(out, PCMK_XE_NODE,
953 : PCMK_XA_TYPE, pcmk__s(type, "cluster"),
954 : PCMK_XA_NAME, pcmk__s(name, ""),
955 : PCMK_XA_ID, pcmk__s(id, ""),
956 : NULL);
957 0 : return pcmk_rc_ok;
958 : }
959 :
960 : PCMK__OUTPUT_ARGS("digests", "const pcmk_resource_t *", "const pcmk_node_t *",
961 : "const char *", "guint", "const pcmk__op_digest_t *")
962 : static int
963 0 : digests_text(pcmk__output_t *out, va_list args)
964 : {
965 0 : const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
966 0 : const pcmk_node_t *node = va_arg(args, const pcmk_node_t *);
967 0 : const char *task = va_arg(args, const char *);
968 0 : guint interval_ms = va_arg(args, guint);
969 0 : const pcmk__op_digest_t *digests = va_arg(args, const pcmk__op_digest_t *);
970 :
971 0 : char *action_desc = NULL;
972 0 : const char *rsc_desc = "unknown resource";
973 0 : const char *node_desc = "unknown node";
974 :
975 0 : if (interval_ms != 0) {
976 0 : action_desc = crm_strdup_printf("%ums-interval %s action", interval_ms,
977 : ((task == NULL)? "unknown" : task));
978 0 : } else if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
979 0 : action_desc = strdup("probe action");
980 : } else {
981 0 : action_desc = crm_strdup_printf("%s action",
982 : ((task == NULL)? "unknown" : task));
983 : }
984 0 : if ((rsc != NULL) && (rsc->id != NULL)) {
985 0 : rsc_desc = rsc->id;
986 : }
987 0 : if ((node != NULL) && (node->details->uname != NULL)) {
988 0 : node_desc = node->details->uname;
989 : }
990 0 : out->begin_list(out, NULL, NULL, "Digests for %s %s on %s",
991 : rsc_desc, action_desc, node_desc);
992 0 : free(action_desc);
993 :
994 0 : if (digests == NULL) {
995 0 : out->list_item(out, NULL, "none");
996 0 : out->end_list(out);
997 0 : return pcmk_rc_ok;
998 : }
999 0 : if (digests->digest_all_calc != NULL) {
1000 0 : out->list_item(out, NULL, "%s (all parameters)",
1001 0 : digests->digest_all_calc);
1002 : }
1003 0 : if (digests->digest_secure_calc != NULL) {
1004 0 : out->list_item(out, NULL, "%s (non-private parameters)",
1005 0 : digests->digest_secure_calc);
1006 : }
1007 0 : if (digests->digest_restart_calc != NULL) {
1008 0 : out->list_item(out, NULL, "%s (non-reloadable parameters)",
1009 0 : digests->digest_restart_calc);
1010 : }
1011 0 : out->end_list(out);
1012 0 : return pcmk_rc_ok;
1013 : }
1014 :
1015 : static void
1016 0 : add_digest_xml(xmlNode *parent, const char *type, const char *digest,
1017 : xmlNode *digest_source)
1018 : {
1019 0 : if (digest != NULL) {
1020 0 : xmlNodePtr digest_xml = pcmk__xe_create(parent, PCMK_XE_DIGEST);
1021 :
1022 0 : crm_xml_add(digest_xml, PCMK_XA_TYPE, pcmk__s(type, "unspecified"));
1023 0 : crm_xml_add(digest_xml, PCMK_XA_HASH, digest);
1024 0 : pcmk__xml_copy(digest_xml, digest_source);
1025 : }
1026 0 : }
1027 :
1028 : PCMK__OUTPUT_ARGS("digests", "const pcmk_resource_t *", "const pcmk_node_t *",
1029 : "const char *", "guint", "const pcmk__op_digest_t *")
1030 : static int
1031 0 : digests_xml(pcmk__output_t *out, va_list args)
1032 : {
1033 0 : const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
1034 0 : const pcmk_node_t *node = va_arg(args, const pcmk_node_t *);
1035 0 : const char *task = va_arg(args, const char *);
1036 0 : guint interval_ms = va_arg(args, guint);
1037 0 : const pcmk__op_digest_t *digests = va_arg(args, const pcmk__op_digest_t *);
1038 :
1039 0 : char *interval_s = crm_strdup_printf("%ums", interval_ms);
1040 0 : xmlNode *xml = NULL;
1041 :
1042 0 : xml = pcmk__output_create_xml_node(out, PCMK_XE_DIGESTS,
1043 0 : PCMK_XA_RESOURCE, pcmk__s(rsc->id, ""),
1044 : PCMK_XA_NODE,
1045 0 : pcmk__s(node->details->uname, ""),
1046 : PCMK_XA_TASK, pcmk__s(task, ""),
1047 : PCMK_XA_INTERVAL, interval_s,
1048 : NULL);
1049 0 : free(interval_s);
1050 0 : if (digests != NULL) {
1051 0 : add_digest_xml(xml, "all", digests->digest_all_calc,
1052 0 : digests->params_all);
1053 0 : add_digest_xml(xml, "nonprivate", digests->digest_secure_calc,
1054 0 : digests->params_secure);
1055 0 : add_digest_xml(xml, "nonreloadable", digests->digest_restart_calc,
1056 0 : digests->params_restart);
1057 : }
1058 0 : return pcmk_rc_ok;
1059 : }
1060 :
1061 : #define STOP_SANITY_ASSERT(lineno) do { \
1062 : if ((current != NULL) && current->details->unclean) { \
1063 : /* It will be a pseudo op */ \
1064 : } else if (stop == NULL) { \
1065 : crm_err("%s:%d: No stop action exists for %s", \
1066 : __func__, lineno, rsc->id); \
1067 : CRM_ASSERT(stop != NULL); \
1068 : } else if (pcmk_is_set(stop->flags, pcmk_action_optional)) { \
1069 : crm_err("%s:%d: Action %s is still optional", \
1070 : __func__, lineno, stop->uuid); \
1071 : CRM_ASSERT(!pcmk_is_set(stop->flags, pcmk_action_optional));\
1072 : } \
1073 : } while (0)
1074 :
1075 : PCMK__OUTPUT_ARGS("rsc-action", "pcmk_resource_t *", "pcmk_node_t *",
1076 : "pcmk_node_t *")
1077 : static int
1078 0 : rsc_action_default(pcmk__output_t *out, va_list args)
1079 : {
1080 0 : pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
1081 0 : pcmk_node_t *current = va_arg(args, pcmk_node_t *);
1082 0 : pcmk_node_t *next = va_arg(args, pcmk_node_t *);
1083 :
1084 0 : GList *possible_matches = NULL;
1085 0 : char *key = NULL;
1086 0 : int rc = pcmk_rc_no_output;
1087 0 : bool moving = false;
1088 :
1089 0 : pcmk_node_t *start_node = NULL;
1090 0 : pcmk_action_t *start = NULL;
1091 0 : pcmk_action_t *stop = NULL;
1092 0 : pcmk_action_t *promote = NULL;
1093 0 : pcmk_action_t *demote = NULL;
1094 0 : pcmk_action_t *reason_op = NULL;
1095 :
1096 0 : if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)
1097 0 : || (current == NULL && next == NULL)) {
1098 0 : const bool managed = pcmk_is_set(rsc->flags, pcmk_rsc_managed);
1099 :
1100 0 : pcmk__rsc_info(rsc, "Leave %s\t(%s%s)",
1101 : rsc->id, pcmk_role_text(rsc->role),
1102 : (managed? "" : " unmanaged"));
1103 0 : return rc;
1104 : }
1105 :
1106 0 : moving = (current != NULL) && (next != NULL)
1107 0 : && !pcmk__same_node(current, next);
1108 :
1109 0 : possible_matches = pe__resource_actions(rsc, next, PCMK_ACTION_START,
1110 : false);
1111 0 : if (possible_matches) {
1112 0 : start = possible_matches->data;
1113 0 : g_list_free(possible_matches);
1114 : }
1115 :
1116 0 : if ((start == NULL)
1117 0 : || !pcmk_is_set(start->flags, pcmk_action_runnable)) {
1118 0 : start_node = NULL;
1119 : } else {
1120 0 : start_node = current;
1121 : }
1122 0 : possible_matches = pe__resource_actions(rsc, start_node, PCMK_ACTION_STOP,
1123 : false);
1124 0 : if (possible_matches) {
1125 0 : stop = possible_matches->data;
1126 0 : g_list_free(possible_matches);
1127 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_stop_unexpected)) {
1128 : /* The resource is multiply active with PCMK_META_MULTIPLE_ACTIVE set to
1129 : * PCMK_VALUE_STOP_UNEXPECTED, and not stopping on its current node, but
1130 : * it should be stopping elsewhere.
1131 : */
1132 0 : possible_matches = pe__resource_actions(rsc, NULL, PCMK_ACTION_STOP,
1133 : false);
1134 0 : if (possible_matches != NULL) {
1135 0 : stop = possible_matches->data;
1136 0 : g_list_free(possible_matches);
1137 : }
1138 : }
1139 :
1140 0 : possible_matches = pe__resource_actions(rsc, next, PCMK_ACTION_PROMOTE,
1141 : false);
1142 0 : if (possible_matches) {
1143 0 : promote = possible_matches->data;
1144 0 : g_list_free(possible_matches);
1145 : }
1146 :
1147 0 : possible_matches = pe__resource_actions(rsc, next, PCMK_ACTION_DEMOTE,
1148 : false);
1149 0 : if (possible_matches) {
1150 0 : demote = possible_matches->data;
1151 0 : g_list_free(possible_matches);
1152 : }
1153 :
1154 0 : if (rsc->role == rsc->next_role) {
1155 0 : pcmk_action_t *migrate_op = NULL;
1156 :
1157 0 : CRM_CHECK(next != NULL, return rc);
1158 :
1159 0 : possible_matches = pe__resource_actions(rsc, next,
1160 : PCMK_ACTION_MIGRATE_FROM,
1161 : false);
1162 0 : if (possible_matches) {
1163 0 : migrate_op = possible_matches->data;
1164 : }
1165 :
1166 0 : if ((migrate_op != NULL) && (current != NULL)
1167 0 : && pcmk_is_set(migrate_op->flags, pcmk_action_runnable)) {
1168 0 : rc = out->message(out, "rsc-action-item", "Migrate", rsc, current,
1169 : next, start, NULL);
1170 :
1171 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_reload)) {
1172 0 : rc = out->message(out, "rsc-action-item", "Reload", rsc, current,
1173 : next, start, NULL);
1174 :
1175 0 : } else if ((start == NULL)
1176 0 : || pcmk_is_set(start->flags, pcmk_action_optional)) {
1177 0 : if ((demote != NULL) && (promote != NULL)
1178 0 : && !pcmk_is_set(demote->flags, pcmk_action_optional)
1179 0 : && !pcmk_is_set(promote->flags, pcmk_action_optional)) {
1180 0 : rc = out->message(out, "rsc-action-item", "Re-promote", rsc,
1181 : current, next, promote, demote);
1182 : } else {
1183 0 : pcmk__rsc_info(rsc, "Leave %s\t(%s %s)", rsc->id,
1184 : pcmk_role_text(rsc->role),
1185 : pcmk__node_name(next));
1186 : }
1187 :
1188 0 : } else if (!pcmk_is_set(start->flags, pcmk_action_runnable)) {
1189 0 : if ((stop == NULL) || (stop->reason == NULL)) {
1190 0 : reason_op = start;
1191 : } else {
1192 0 : reason_op = stop;
1193 : }
1194 0 : rc = out->message(out, "rsc-action-item", "Stop", rsc, current,
1195 : NULL, stop, reason_op);
1196 0 : STOP_SANITY_ASSERT(__LINE__);
1197 :
1198 0 : } else if (moving && current) {
1199 0 : const bool failed = pcmk_is_set(rsc->flags, pcmk_rsc_failed);
1200 :
1201 0 : rc = out->message(out, "rsc-action-item",
1202 : (failed? "Recover" : "Move"), rsc, current, next,
1203 : stop, NULL);
1204 :
1205 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
1206 0 : rc = out->message(out, "rsc-action-item", "Recover", rsc, current,
1207 : NULL, stop, NULL);
1208 0 : STOP_SANITY_ASSERT(__LINE__);
1209 :
1210 : } else {
1211 0 : rc = out->message(out, "rsc-action-item", "Restart", rsc, current,
1212 : next, start, NULL);
1213 : #if 0
1214 : /* @TODO This can be reached in situations that should really be
1215 : * "Start" (see for example the migrate-fail-7 regression test)
1216 : */
1217 : STOP_SANITY_ASSERT(__LINE__);
1218 : #endif
1219 : }
1220 :
1221 0 : g_list_free(possible_matches);
1222 0 : return rc;
1223 : }
1224 :
1225 0 : if ((stop != NULL)
1226 0 : && ((rsc->next_role == pcmk_role_stopped)
1227 0 : || ((start != NULL)
1228 0 : && !pcmk_is_set(start->flags, pcmk_action_runnable)))) {
1229 :
1230 0 : key = stop_key(rsc);
1231 0 : for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) {
1232 0 : pcmk_node_t *node = iter->data;
1233 0 : pcmk_action_t *stop_op = NULL;
1234 :
1235 0 : reason_op = start;
1236 0 : possible_matches = find_actions(rsc->actions, key, node);
1237 0 : if (possible_matches) {
1238 0 : stop_op = possible_matches->data;
1239 0 : g_list_free(possible_matches);
1240 : }
1241 :
1242 0 : if (stop_op != NULL) {
1243 0 : if (pcmk_is_set(stop_op->flags, pcmk_action_runnable)) {
1244 0 : STOP_SANITY_ASSERT(__LINE__);
1245 : }
1246 0 : if (stop_op->reason != NULL) {
1247 0 : reason_op = stop_op;
1248 : }
1249 : }
1250 :
1251 0 : if (out->message(out, "rsc-action-item", "Stop", rsc, node, NULL,
1252 : stop_op, reason_op) == pcmk_rc_ok) {
1253 0 : rc = pcmk_rc_ok;
1254 : }
1255 : }
1256 :
1257 0 : free(key);
1258 :
1259 0 : } else if ((stop != NULL)
1260 0 : && pcmk_all_flags_set(rsc->flags,
1261 : pcmk_rsc_failed|pcmk_rsc_stop_if_failed)) {
1262 : /* 'stop' may be NULL if the failure was ignored */
1263 0 : rc = out->message(out, "rsc-action-item", "Recover", rsc, current,
1264 : next, stop, start);
1265 0 : STOP_SANITY_ASSERT(__LINE__);
1266 :
1267 0 : } else if (moving) {
1268 0 : rc = out->message(out, "rsc-action-item", "Move", rsc, current, next,
1269 : stop, NULL);
1270 0 : STOP_SANITY_ASSERT(__LINE__);
1271 :
1272 0 : } else if (pcmk_is_set(rsc->flags, pcmk_rsc_reload)) {
1273 0 : rc = out->message(out, "rsc-action-item", "Reload", rsc, current, next,
1274 : start, NULL);
1275 :
1276 0 : } else if ((stop != NULL)
1277 0 : && !pcmk_is_set(stop->flags, pcmk_action_optional)) {
1278 0 : rc = out->message(out, "rsc-action-item", "Restart", rsc, current,
1279 : next, start, NULL);
1280 0 : STOP_SANITY_ASSERT(__LINE__);
1281 :
1282 0 : } else if (rsc->role == pcmk_role_promoted) {
1283 0 : CRM_LOG_ASSERT(current != NULL);
1284 0 : rc = out->message(out, "rsc-action-item", "Demote", rsc, current,
1285 : next, demote, NULL);
1286 :
1287 0 : } else if (rsc->next_role == pcmk_role_promoted) {
1288 0 : CRM_LOG_ASSERT(next);
1289 0 : rc = out->message(out, "rsc-action-item", "Promote", rsc, current,
1290 : next, promote, NULL);
1291 :
1292 0 : } else if ((rsc->role == pcmk_role_stopped)
1293 0 : && (rsc->next_role > pcmk_role_stopped)) {
1294 0 : rc = out->message(out, "rsc-action-item", "Start", rsc, current, next,
1295 : start, NULL);
1296 : }
1297 :
1298 0 : return rc;
1299 : }
1300 :
1301 : PCMK__OUTPUT_ARGS("node-action", "const char *", "const char *", "const char *")
1302 : static int
1303 0 : node_action(pcmk__output_t *out, va_list args)
1304 : {
1305 0 : const char *task = va_arg(args, const char *);
1306 0 : const char *node_name = va_arg(args, const char *);
1307 0 : const char *reason = va_arg(args, const char *);
1308 :
1309 0 : if (task == NULL) {
1310 0 : return pcmk_rc_no_output;
1311 0 : } else if (reason) {
1312 0 : out->list_item(out, NULL, "%s %s '%s'", task, node_name, reason);
1313 : } else {
1314 0 : crm_notice(" * %s %s", task, node_name);
1315 : }
1316 :
1317 0 : return pcmk_rc_ok;
1318 : }
1319 :
1320 : PCMK__OUTPUT_ARGS("node-action", "const char *", "const char *", "const char *")
1321 : static int
1322 0 : node_action_xml(pcmk__output_t *out, va_list args)
1323 : {
1324 0 : const char *task = va_arg(args, const char *);
1325 0 : const char *node_name = va_arg(args, const char *);
1326 0 : const char *reason = va_arg(args, const char *);
1327 :
1328 0 : if (task == NULL) {
1329 0 : return pcmk_rc_no_output;
1330 0 : } else if (reason) {
1331 0 : pcmk__output_create_xml_node(out, PCMK_XE_NODE_ACTION,
1332 : PCMK_XA_TASK, task,
1333 : PCMK_XA_NODE, node_name,
1334 : PCMK_XA_REASON, reason,
1335 : NULL);
1336 : } else {
1337 0 : crm_notice(" * %s %s", task, node_name);
1338 : }
1339 :
1340 0 : return pcmk_rc_ok;
1341 : }
1342 :
1343 : PCMK__OUTPUT_ARGS("node-info", "uint32_t", "const char *", "const char *",
1344 : "const char *", "bool", "bool")
1345 : static int
1346 0 : node_info_default(pcmk__output_t *out, va_list args)
1347 : {
1348 0 : uint32_t node_id = va_arg(args, uint32_t);
1349 0 : const char *node_name = va_arg(args, const char *);
1350 0 : const char *uuid = va_arg(args, const char *);
1351 0 : const char *state = va_arg(args, const char *);
1352 0 : bool have_quorum = (bool) va_arg(args, int);
1353 0 : bool is_remote = (bool) va_arg(args, int);
1354 :
1355 0 : return out->info(out,
1356 : "Node %" PRIu32 ": %s "
1357 : "(uuid=%s, state=%s, have_quorum=%s, is_remote=%s)",
1358 : node_id, pcmk__s(node_name, "unknown"),
1359 : pcmk__s(uuid, "unknown"), pcmk__s(state, "unknown"),
1360 : pcmk__btoa(have_quorum), pcmk__btoa(is_remote));
1361 : }
1362 :
1363 : PCMK__OUTPUT_ARGS("node-info", "uint32_t", "const char *", "const char *",
1364 : "const char *", "bool", "bool")
1365 : static int
1366 0 : node_info_xml(pcmk__output_t *out, va_list args)
1367 : {
1368 0 : uint32_t node_id = va_arg(args, uint32_t);
1369 0 : const char *node_name = va_arg(args, const char *);
1370 0 : const char *uuid = va_arg(args, const char *);
1371 0 : const char *state = va_arg(args, const char *);
1372 0 : bool have_quorum = (bool) va_arg(args, int);
1373 0 : bool is_remote = (bool) va_arg(args, int);
1374 :
1375 0 : char *id_s = crm_strdup_printf("%" PRIu32, node_id);
1376 :
1377 0 : pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO,
1378 : PCMK_XA_NODEID, id_s,
1379 : PCMK_XA_UNAME, node_name,
1380 : PCMK_XA_ID, uuid,
1381 : PCMK_XA_CRMD, state,
1382 : PCMK_XA_HAVE_QUORUM, pcmk__btoa(have_quorum),
1383 : PCMK_XA_REMOTE_NODE, pcmk__btoa(is_remote),
1384 : NULL);
1385 0 : free(id_s);
1386 0 : return pcmk_rc_ok;
1387 : }
1388 :
1389 : PCMK__OUTPUT_ARGS("inject-cluster-action", "const char *", "const char *",
1390 : "xmlNode *")
1391 : static int
1392 0 : inject_cluster_action(pcmk__output_t *out, va_list args)
1393 : {
1394 0 : const char *node = va_arg(args, const char *);
1395 0 : const char *task = va_arg(args, const char *);
1396 0 : xmlNodePtr rsc = va_arg(args, xmlNodePtr);
1397 :
1398 0 : if (out->is_quiet(out)) {
1399 0 : return pcmk_rc_no_output;
1400 : }
1401 :
1402 0 : if (rsc != NULL) {
1403 0 : out->list_item(out, NULL, "Cluster action: %s for %s on %s",
1404 : task, pcmk__xe_id(rsc), node);
1405 : } else {
1406 0 : out->list_item(out, NULL, "Cluster action: %s on %s", task, node);
1407 : }
1408 :
1409 0 : return pcmk_rc_ok;
1410 : }
1411 :
1412 : PCMK__OUTPUT_ARGS("inject-cluster-action", "const char *", "const char *",
1413 : "xmlNode *")
1414 : static int
1415 0 : inject_cluster_action_xml(pcmk__output_t *out, va_list args)
1416 : {
1417 0 : const char *node = va_arg(args, const char *);
1418 0 : const char *task = va_arg(args, const char *);
1419 0 : xmlNodePtr rsc = va_arg(args, xmlNodePtr);
1420 :
1421 0 : xmlNodePtr xml_node = NULL;
1422 :
1423 0 : if (out->is_quiet(out)) {
1424 0 : return pcmk_rc_no_output;
1425 : }
1426 :
1427 0 : xml_node = pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_ACTION,
1428 : PCMK_XA_TASK, task,
1429 : PCMK_XA_NODE, node,
1430 : NULL);
1431 :
1432 0 : if (rsc) {
1433 0 : crm_xml_add(xml_node, PCMK_XA_ID, pcmk__xe_id(rsc));
1434 : }
1435 :
1436 0 : return pcmk_rc_ok;
1437 : }
1438 :
1439 : PCMK__OUTPUT_ARGS("inject-fencing-action", "const char *", "const char *")
1440 : static int
1441 0 : inject_fencing_action(pcmk__output_t *out, va_list args)
1442 : {
1443 0 : const char *target = va_arg(args, const char *);
1444 0 : const char *op = va_arg(args, const char *);
1445 :
1446 0 : if (out->is_quiet(out)) {
1447 0 : return pcmk_rc_no_output;
1448 : }
1449 :
1450 0 : out->list_item(out, NULL, "Fencing %s (%s)", target, op);
1451 0 : return pcmk_rc_ok;
1452 : }
1453 :
1454 : PCMK__OUTPUT_ARGS("inject-fencing-action", "const char *", "const char *")
1455 : static int
1456 0 : inject_fencing_action_xml(pcmk__output_t *out, va_list args)
1457 : {
1458 0 : const char *target = va_arg(args, const char *);
1459 0 : const char *op = va_arg(args, const char *);
1460 :
1461 0 : if (out->is_quiet(out)) {
1462 0 : return pcmk_rc_no_output;
1463 : }
1464 :
1465 0 : pcmk__output_create_xml_node(out, PCMK_XE_FENCING_ACTION,
1466 : PCMK_XA_TARGET, target,
1467 : PCMK_XA_OP, op,
1468 : NULL);
1469 0 : return pcmk_rc_ok;
1470 : }
1471 :
1472 : PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNode *")
1473 : static int
1474 0 : inject_attr(pcmk__output_t *out, va_list args)
1475 : {
1476 0 : const char *name = va_arg(args, const char *);
1477 0 : const char *value = va_arg(args, const char *);
1478 0 : xmlNodePtr cib_node = va_arg(args, xmlNodePtr);
1479 :
1480 0 : xmlChar *node_path = NULL;
1481 :
1482 0 : if (out->is_quiet(out)) {
1483 0 : return pcmk_rc_no_output;
1484 : }
1485 :
1486 0 : node_path = xmlGetNodePath(cib_node);
1487 :
1488 0 : out->list_item(out, NULL, "Injecting attribute %s=%s into %s '%s'",
1489 : name, value, node_path, pcmk__xe_id(cib_node));
1490 :
1491 0 : free(node_path);
1492 0 : return pcmk_rc_ok;
1493 : }
1494 :
1495 : PCMK__OUTPUT_ARGS("inject-attr", "const char *", "const char *", "xmlNode *")
1496 : static int
1497 0 : inject_attr_xml(pcmk__output_t *out, va_list args)
1498 : {
1499 0 : const char *name = va_arg(args, const char *);
1500 0 : const char *value = va_arg(args, const char *);
1501 0 : xmlNodePtr cib_node = va_arg(args, xmlNodePtr);
1502 :
1503 0 : xmlChar *node_path = NULL;
1504 :
1505 0 : if (out->is_quiet(out)) {
1506 0 : return pcmk_rc_no_output;
1507 : }
1508 :
1509 0 : node_path = xmlGetNodePath(cib_node);
1510 :
1511 0 : pcmk__output_create_xml_node(out, PCMK_XE_INJECT_ATTR,
1512 : PCMK_XA_NAME, name,
1513 : PCMK_XA_VALUE, value,
1514 : PCMK_XA_NODE_PATH, node_path,
1515 : PCMK_XA_CIB_NODE, pcmk__xe_id(cib_node),
1516 : NULL);
1517 0 : free(node_path);
1518 0 : return pcmk_rc_ok;
1519 : }
1520 :
1521 : PCMK__OUTPUT_ARGS("inject-spec", "const char *")
1522 : static int
1523 0 : inject_spec(pcmk__output_t *out, va_list args)
1524 : {
1525 0 : const char *spec = va_arg(args, const char *);
1526 :
1527 0 : if (out->is_quiet(out)) {
1528 0 : return pcmk_rc_no_output;
1529 : }
1530 :
1531 0 : out->list_item(out, NULL, "Injecting %s into the configuration", spec);
1532 0 : return pcmk_rc_ok;
1533 : }
1534 :
1535 : PCMK__OUTPUT_ARGS("inject-spec", "const char *")
1536 : static int
1537 0 : inject_spec_xml(pcmk__output_t *out, va_list args)
1538 : {
1539 0 : const char *spec = va_arg(args, const char *);
1540 :
1541 0 : if (out->is_quiet(out)) {
1542 0 : return pcmk_rc_no_output;
1543 : }
1544 :
1545 0 : pcmk__output_create_xml_node(out, PCMK_XE_INJECT_SPEC,
1546 : PCMK_XA_SPEC, spec,
1547 : NULL);
1548 0 : return pcmk_rc_ok;
1549 : }
1550 :
1551 : PCMK__OUTPUT_ARGS("inject-modify-config", "const char *", "const char *")
1552 : static int
1553 0 : inject_modify_config(pcmk__output_t *out, va_list args)
1554 : {
1555 0 : const char *quorum = va_arg(args, const char *);
1556 0 : const char *watchdog = va_arg(args, const char *);
1557 :
1558 0 : if (out->is_quiet(out)) {
1559 0 : return pcmk_rc_no_output;
1560 : }
1561 :
1562 0 : out->begin_list(out, NULL, NULL, "Performing Requested Modifications");
1563 :
1564 0 : if (quorum) {
1565 0 : out->list_item(out, NULL, "Setting quorum: %s", quorum);
1566 : }
1567 :
1568 0 : if (watchdog) {
1569 0 : out->list_item(out, NULL, "Setting watchdog: %s", watchdog);
1570 : }
1571 :
1572 0 : return pcmk_rc_ok;
1573 : }
1574 :
1575 : PCMK__OUTPUT_ARGS("inject-modify-config", "const char *", "const char *")
1576 : static int
1577 0 : inject_modify_config_xml(pcmk__output_t *out, va_list args)
1578 : {
1579 0 : const char *quorum = va_arg(args, const char *);
1580 0 : const char *watchdog = va_arg(args, const char *);
1581 :
1582 0 : xmlNodePtr node = NULL;
1583 :
1584 0 : if (out->is_quiet(out)) {
1585 0 : return pcmk_rc_no_output;
1586 : }
1587 :
1588 0 : node = pcmk__output_xml_create_parent(out, PCMK_XE_MODIFICATIONS, NULL);
1589 :
1590 0 : if (quorum) {
1591 0 : crm_xml_add(node, PCMK_XA_QUORUM, quorum);
1592 : }
1593 :
1594 0 : if (watchdog) {
1595 0 : crm_xml_add(node, PCMK_XA_WATCHDOG, watchdog);
1596 : }
1597 :
1598 0 : pcmk__output_xml_pop_parent(out);
1599 0 : return pcmk_rc_ok;
1600 : }
1601 :
1602 : PCMK__OUTPUT_ARGS("inject-modify-node", "const char *", "const char *")
1603 : static int
1604 0 : inject_modify_node(pcmk__output_t *out, va_list args)
1605 : {
1606 0 : const char *action = va_arg(args, const char *);
1607 0 : const char *node = va_arg(args, const char *);
1608 :
1609 0 : if (out->is_quiet(out)) {
1610 0 : return pcmk_rc_no_output;
1611 : }
1612 :
1613 0 : if (pcmk__str_eq(action, "Online", pcmk__str_none)) {
1614 0 : out->list_item(out, NULL, "Bringing node %s online", node);
1615 0 : return pcmk_rc_ok;
1616 0 : } else if (pcmk__str_eq(action, "Offline", pcmk__str_none)) {
1617 0 : out->list_item(out, NULL, "Taking node %s offline", node);
1618 0 : return pcmk_rc_ok;
1619 0 : } else if (pcmk__str_eq(action, "Failing", pcmk__str_none)) {
1620 0 : out->list_item(out, NULL, "Failing node %s", node);
1621 0 : return pcmk_rc_ok;
1622 : }
1623 :
1624 0 : return pcmk_rc_no_output;
1625 : }
1626 :
1627 : PCMK__OUTPUT_ARGS("inject-modify-node", "const char *", "const char *")
1628 : static int
1629 0 : inject_modify_node_xml(pcmk__output_t *out, va_list args)
1630 : {
1631 0 : const char *action = va_arg(args, const char *);
1632 0 : const char *node = va_arg(args, const char *);
1633 :
1634 0 : if (out->is_quiet(out)) {
1635 0 : return pcmk_rc_no_output;
1636 : }
1637 :
1638 0 : pcmk__output_create_xml_node(out, PCMK_XE_MODIFY_NODE,
1639 : PCMK_XA_ACTION, action,
1640 : PCMK_XA_NODE, node,
1641 : NULL);
1642 0 : return pcmk_rc_ok;
1643 : }
1644 :
1645 : PCMK__OUTPUT_ARGS("inject-modify-ticket", "const char *", "const char *")
1646 : static int
1647 0 : inject_modify_ticket(pcmk__output_t *out, va_list args)
1648 : {
1649 0 : const char *action = va_arg(args, const char *);
1650 0 : const char *ticket = va_arg(args, const char *);
1651 :
1652 0 : if (out->is_quiet(out)) {
1653 0 : return pcmk_rc_no_output;
1654 : }
1655 :
1656 0 : if (pcmk__str_eq(action, "Standby", pcmk__str_none)) {
1657 0 : out->list_item(out, NULL, "Making ticket %s standby", ticket);
1658 : } else {
1659 0 : out->list_item(out, NULL, "%s ticket %s", action, ticket);
1660 : }
1661 :
1662 0 : return pcmk_rc_ok;
1663 : }
1664 :
1665 : PCMK__OUTPUT_ARGS("inject-modify-ticket", "const char *", "const char *")
1666 : static int
1667 0 : inject_modify_ticket_xml(pcmk__output_t *out, va_list args)
1668 : {
1669 0 : const char *action = va_arg(args, const char *);
1670 0 : const char *ticket = va_arg(args, const char *);
1671 :
1672 0 : if (out->is_quiet(out)) {
1673 0 : return pcmk_rc_no_output;
1674 : }
1675 :
1676 0 : pcmk__output_create_xml_node(out, PCMK_XE_MODIFY_TICKET,
1677 : PCMK_XA_ACTION, action,
1678 : PCMK_XA_TICKET, ticket,
1679 : NULL);
1680 0 : return pcmk_rc_ok;
1681 : }
1682 :
1683 : PCMK__OUTPUT_ARGS("inject-pseudo-action", "const char *", "const char *")
1684 : static int
1685 0 : inject_pseudo_action(pcmk__output_t *out, va_list args)
1686 : {
1687 0 : const char *node = va_arg(args, const char *);
1688 0 : const char *task = va_arg(args, const char *);
1689 :
1690 0 : if (out->is_quiet(out)) {
1691 0 : return pcmk_rc_no_output;
1692 : }
1693 :
1694 0 : out->list_item(out, NULL, "Pseudo action: %s%s%s",
1695 : task, ((node == NULL)? "" : " on "), pcmk__s(node, ""));
1696 0 : return pcmk_rc_ok;
1697 : }
1698 :
1699 : PCMK__OUTPUT_ARGS("inject-pseudo-action", "const char *", "const char *")
1700 : static int
1701 0 : inject_pseudo_action_xml(pcmk__output_t *out, va_list args)
1702 : {
1703 0 : const char *node = va_arg(args, const char *);
1704 0 : const char *task = va_arg(args, const char *);
1705 :
1706 0 : xmlNodePtr xml_node = NULL;
1707 :
1708 0 : if (out->is_quiet(out)) {
1709 0 : return pcmk_rc_no_output;
1710 : }
1711 :
1712 0 : xml_node = pcmk__output_create_xml_node(out, PCMK_XE_PSEUDO_ACTION,
1713 : PCMK_XA_TASK, task,
1714 : NULL);
1715 0 : if (node) {
1716 0 : crm_xml_add(xml_node, PCMK_XA_NODE, node);
1717 : }
1718 :
1719 0 : return pcmk_rc_ok;
1720 : }
1721 :
1722 : PCMK__OUTPUT_ARGS("inject-rsc-action", "const char *", "const char *",
1723 : "const char *", "guint")
1724 : static int
1725 0 : inject_rsc_action(pcmk__output_t *out, va_list args)
1726 : {
1727 0 : const char *rsc = va_arg(args, const char *);
1728 0 : const char *operation = va_arg(args, const char *);
1729 0 : const char *node = va_arg(args, const char *);
1730 0 : guint interval_ms = va_arg(args, guint);
1731 :
1732 0 : if (out->is_quiet(out)) {
1733 0 : return pcmk_rc_no_output;
1734 : }
1735 :
1736 0 : if (interval_ms) {
1737 0 : out->list_item(out, NULL, "Resource action: %-15s %s=%u on %s",
1738 : rsc, operation, interval_ms, node);
1739 : } else {
1740 0 : out->list_item(out, NULL, "Resource action: %-15s %s on %s",
1741 : rsc, operation, node);
1742 : }
1743 :
1744 0 : return pcmk_rc_ok;
1745 : }
1746 :
1747 : PCMK__OUTPUT_ARGS("inject-rsc-action", "const char *", "const char *",
1748 : "const char *", "guint")
1749 : static int
1750 0 : inject_rsc_action_xml(pcmk__output_t *out, va_list args)
1751 : {
1752 0 : const char *rsc = va_arg(args, const char *);
1753 0 : const char *operation = va_arg(args, const char *);
1754 0 : const char *node = va_arg(args, const char *);
1755 0 : guint interval_ms = va_arg(args, guint);
1756 :
1757 0 : xmlNodePtr xml_node = NULL;
1758 :
1759 0 : if (out->is_quiet(out)) {
1760 0 : return pcmk_rc_no_output;
1761 : }
1762 :
1763 0 : xml_node = pcmk__output_create_xml_node(out, PCMK_XE_RSC_ACTION,
1764 : PCMK_XA_RESOURCE, rsc,
1765 : PCMK_XA_OP, operation,
1766 : PCMK_XA_NODE, node,
1767 : NULL);
1768 :
1769 0 : if (interval_ms) {
1770 0 : char *interval_s = pcmk__itoa(interval_ms);
1771 :
1772 0 : crm_xml_add(xml_node, PCMK_XA_INTERVAL, interval_s);
1773 0 : free(interval_s);
1774 : }
1775 :
1776 0 : return pcmk_rc_ok;
1777 : }
1778 :
1779 : #define CHECK_RC(retcode, retval) \
1780 : if (retval == pcmk_rc_ok) { \
1781 : retcode = pcmk_rc_ok; \
1782 : }
1783 :
1784 : PCMK__OUTPUT_ARGS("cluster-status", "pcmk_scheduler_t *",
1785 : "enum pcmk_pacemakerd_state", "crm_exit_t",
1786 : "stonith_history_t *", "enum pcmk__fence_history", "uint32_t",
1787 : "uint32_t", "const char *", "GList *", "GList *")
1788 : int
1789 0 : pcmk__cluster_status_text(pcmk__output_t *out, va_list args)
1790 : {
1791 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1792 0 : enum pcmk_pacemakerd_state pcmkd_state =
1793 : (enum pcmk_pacemakerd_state) va_arg(args, int);
1794 0 : crm_exit_t history_rc = va_arg(args, crm_exit_t);
1795 0 : stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
1796 0 : enum pcmk__fence_history fence_history = va_arg(args, int);
1797 0 : uint32_t section_opts = va_arg(args, uint32_t);
1798 0 : uint32_t show_opts = va_arg(args, uint32_t);
1799 0 : const char *prefix = va_arg(args, const char *);
1800 0 : GList *unames = va_arg(args, GList *);
1801 0 : GList *resources = va_arg(args, GList *);
1802 :
1803 0 : int rc = pcmk_rc_no_output;
1804 0 : bool already_printed_failure = false;
1805 :
1806 0 : CHECK_RC(rc, out->message(out, "cluster-summary", scheduler, pcmkd_state,
1807 : section_opts, show_opts));
1808 :
1809 0 : if (pcmk_is_set(section_opts, pcmk_section_nodes) && unames) {
1810 0 : CHECK_RC(rc, out->message(out, "node-list", scheduler->nodes, unames,
1811 : resources, show_opts, rc == pcmk_rc_ok));
1812 : }
1813 :
1814 : /* Print resources section, if needed */
1815 0 : if (pcmk_is_set(section_opts, pcmk_section_resources)) {
1816 0 : CHECK_RC(rc, out->message(out, "resource-list", scheduler, show_opts,
1817 : true, unames, resources, rc == pcmk_rc_ok));
1818 : }
1819 :
1820 : /* print Node Attributes section if requested */
1821 0 : if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
1822 0 : CHECK_RC(rc, out->message(out, "node-attribute-list", scheduler,
1823 : show_opts, (rc == pcmk_rc_ok), unames,
1824 : resources));
1825 : }
1826 :
1827 : /* If requested, print resource operations (which includes failcounts)
1828 : * or just failcounts
1829 : */
1830 0 : if (pcmk_any_flags_set(section_opts,
1831 : pcmk_section_operations|pcmk_section_failcounts)) {
1832 0 : CHECK_RC(rc, out->message(out, "node-summary", scheduler, unames,
1833 : resources, section_opts, show_opts,
1834 : (rc == pcmk_rc_ok)));
1835 : }
1836 :
1837 : /* If there were any failed actions, print them */
1838 0 : if (pcmk_is_set(section_opts, pcmk_section_failures)
1839 0 : && (scheduler->failed != NULL)
1840 0 : && (scheduler->failed->children != NULL)) {
1841 :
1842 0 : CHECK_RC(rc, out->message(out, "failed-action-list", scheduler, unames,
1843 : resources, show_opts, rc == pcmk_rc_ok));
1844 : }
1845 :
1846 : /* Print failed stonith actions */
1847 0 : if (pcmk_is_set(section_opts, pcmk_section_fence_failed) &&
1848 : fence_history != pcmk__fence_history_none) {
1849 0 : if (history_rc == 0) {
1850 0 : stonith_history_t *hp = NULL;
1851 :
1852 0 : hp = stonith__first_matching_event(stonith_history,
1853 : stonith__event_state_eq,
1854 : GINT_TO_POINTER(st_failed));
1855 0 : if (hp) {
1856 0 : CHECK_RC(rc, out->message(out, "failed-fencing-list",
1857 : stonith_history, unames, section_opts,
1858 : show_opts, rc == pcmk_rc_ok));
1859 : }
1860 : } else {
1861 0 : PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
1862 0 : out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
1863 0 : out->list_item(out, NULL, "Failed to get fencing history: %s",
1864 : crm_exit_str(history_rc));
1865 0 : out->end_list(out);
1866 :
1867 0 : already_printed_failure = true;
1868 : }
1869 : }
1870 :
1871 : /* Print tickets if requested */
1872 0 : if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
1873 0 : CHECK_RC(rc, out->message(out, "ticket-list", scheduler->tickets,
1874 : (rc == pcmk_rc_ok), false, false));
1875 : }
1876 :
1877 : /* Print negative location constraints if requested */
1878 0 : if (pcmk_is_set(section_opts, pcmk_section_bans)) {
1879 0 : CHECK_RC(rc, out->message(out, "ban-list", scheduler, prefix, resources,
1880 : show_opts, rc == pcmk_rc_ok));
1881 : }
1882 :
1883 : /* Print stonith history */
1884 0 : if (pcmk_any_flags_set(section_opts, pcmk_section_fencing_all) &&
1885 : fence_history != pcmk__fence_history_none) {
1886 0 : if (history_rc != 0) {
1887 0 : if (!already_printed_failure) {
1888 0 : PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
1889 0 : out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
1890 0 : out->list_item(out, NULL, "Failed to get fencing history: %s",
1891 : crm_exit_str(history_rc));
1892 0 : out->end_list(out);
1893 : }
1894 0 : } else if (pcmk_is_set(section_opts, pcmk_section_fence_worked)) {
1895 0 : stonith_history_t *hp = NULL;
1896 :
1897 0 : hp = stonith__first_matching_event(stonith_history,
1898 : stonith__event_state_neq,
1899 : GINT_TO_POINTER(st_failed));
1900 0 : if (hp) {
1901 0 : CHECK_RC(rc, out->message(out, "fencing-list", hp, unames,
1902 : section_opts, show_opts,
1903 : rc == pcmk_rc_ok));
1904 : }
1905 0 : } else if (pcmk_is_set(section_opts, pcmk_section_fence_pending)) {
1906 0 : stonith_history_t *hp = NULL;
1907 :
1908 0 : hp = stonith__first_matching_event(stonith_history,
1909 : stonith__event_state_pending,
1910 : NULL);
1911 0 : if (hp) {
1912 0 : CHECK_RC(rc, out->message(out, "pending-fencing-list", hp,
1913 : unames, section_opts, show_opts,
1914 : rc == pcmk_rc_ok));
1915 : }
1916 : }
1917 : }
1918 :
1919 0 : return rc;
1920 : }
1921 :
1922 : PCMK__OUTPUT_ARGS("cluster-status", "pcmk_scheduler_t *",
1923 : "enum pcmk_pacemakerd_state", "crm_exit_t",
1924 : "stonith_history_t *", "enum pcmk__fence_history", "uint32_t",
1925 : "uint32_t", "const char *", "GList *", "GList *")
1926 : static int
1927 0 : cluster_status_xml(pcmk__output_t *out, va_list args)
1928 : {
1929 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
1930 0 : enum pcmk_pacemakerd_state pcmkd_state =
1931 : (enum pcmk_pacemakerd_state) va_arg(args, int);
1932 0 : crm_exit_t history_rc = va_arg(args, crm_exit_t);
1933 0 : stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
1934 0 : enum pcmk__fence_history fence_history = va_arg(args, int);
1935 0 : uint32_t section_opts = va_arg(args, uint32_t);
1936 0 : uint32_t show_opts = va_arg(args, uint32_t);
1937 0 : const char *prefix = va_arg(args, const char *);
1938 0 : GList *unames = va_arg(args, GList *);
1939 0 : GList *resources = va_arg(args, GList *);
1940 :
1941 0 : out->message(out, "cluster-summary", scheduler, pcmkd_state, section_opts,
1942 : show_opts);
1943 :
1944 : /*** NODES ***/
1945 0 : if (pcmk_is_set(section_opts, pcmk_section_nodes)) {
1946 0 : out->message(out, "node-list", scheduler->nodes, unames, resources,
1947 : show_opts, false);
1948 : }
1949 :
1950 : /* Print resources section, if needed */
1951 0 : if (pcmk_is_set(section_opts, pcmk_section_resources)) {
1952 : /* XML output always displays full details. */
1953 0 : uint32_t full_show_opts = show_opts & ~pcmk_show_brief;
1954 :
1955 0 : out->message(out, "resource-list", scheduler, full_show_opts,
1956 : false, unames, resources, false);
1957 : }
1958 :
1959 : /* print Node Attributes section if requested */
1960 0 : if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
1961 0 : out->message(out, "node-attribute-list", scheduler, show_opts, false,
1962 : unames, resources);
1963 : }
1964 :
1965 : /* If requested, print resource operations (which includes failcounts)
1966 : * or just failcounts
1967 : */
1968 0 : if (pcmk_any_flags_set(section_opts,
1969 : pcmk_section_operations|pcmk_section_failcounts)) {
1970 0 : out->message(out, "node-summary", scheduler, unames,
1971 : resources, section_opts, show_opts, false);
1972 : }
1973 :
1974 : /* If there were any failed actions, print them */
1975 0 : if (pcmk_is_set(section_opts, pcmk_section_failures)
1976 0 : && (scheduler->failed != NULL)
1977 0 : && (scheduler->failed->children != NULL)) {
1978 :
1979 0 : out->message(out, "failed-action-list", scheduler, unames, resources,
1980 : show_opts, false);
1981 : }
1982 :
1983 : /* Print stonith history */
1984 0 : if (pcmk_is_set(section_opts, pcmk_section_fencing_all) &&
1985 : fence_history != pcmk__fence_history_none) {
1986 0 : out->message(out, "full-fencing-list", history_rc, stonith_history,
1987 : unames, section_opts, show_opts, false);
1988 : }
1989 :
1990 : /* Print tickets if requested */
1991 0 : if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
1992 0 : out->message(out, "ticket-list", scheduler->tickets, false, false, false);
1993 : }
1994 :
1995 : /* Print negative location constraints if requested */
1996 0 : if (pcmk_is_set(section_opts, pcmk_section_bans)) {
1997 0 : out->message(out, "ban-list", scheduler, prefix, resources, show_opts,
1998 : false);
1999 : }
2000 :
2001 0 : return pcmk_rc_ok;
2002 : }
2003 :
2004 : PCMK__OUTPUT_ARGS("cluster-status", "pcmk_scheduler_t *",
2005 : "enum pcmk_pacemakerd_state", "crm_exit_t",
2006 : "stonith_history_t *", "enum pcmk__fence_history", "uint32_t",
2007 : "uint32_t", "const char *", "GList *", "GList *")
2008 : static int
2009 0 : cluster_status_html(pcmk__output_t *out, va_list args)
2010 : {
2011 0 : pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
2012 0 : enum pcmk_pacemakerd_state pcmkd_state =
2013 : (enum pcmk_pacemakerd_state) va_arg(args, int);
2014 0 : crm_exit_t history_rc = va_arg(args, crm_exit_t);
2015 0 : stonith_history_t *stonith_history = va_arg(args, stonith_history_t *);
2016 0 : enum pcmk__fence_history fence_history = va_arg(args, int);
2017 0 : uint32_t section_opts = va_arg(args, uint32_t);
2018 0 : uint32_t show_opts = va_arg(args, uint32_t);
2019 0 : const char *prefix = va_arg(args, const char *);
2020 0 : GList *unames = va_arg(args, GList *);
2021 0 : GList *resources = va_arg(args, GList *);
2022 0 : bool already_printed_failure = false;
2023 :
2024 0 : out->message(out, "cluster-summary", scheduler, pcmkd_state, section_opts,
2025 : show_opts);
2026 :
2027 : /*** NODE LIST ***/
2028 0 : if (pcmk_is_set(section_opts, pcmk_section_nodes) && unames) {
2029 0 : out->message(out, "node-list", scheduler->nodes, unames, resources,
2030 : show_opts, false);
2031 : }
2032 :
2033 : /* Print resources section, if needed */
2034 0 : if (pcmk_is_set(section_opts, pcmk_section_resources)) {
2035 0 : out->message(out, "resource-list", scheduler, show_opts, true, unames,
2036 : resources, false);
2037 : }
2038 :
2039 : /* print Node Attributes section if requested */
2040 0 : if (pcmk_is_set(section_opts, pcmk_section_attributes)) {
2041 0 : out->message(out, "node-attribute-list", scheduler, show_opts, false,
2042 : unames, resources);
2043 : }
2044 :
2045 : /* If requested, print resource operations (which includes failcounts)
2046 : * or just failcounts
2047 : */
2048 0 : if (pcmk_any_flags_set(section_opts,
2049 : pcmk_section_operations|pcmk_section_failcounts)) {
2050 0 : out->message(out, "node-summary", scheduler, unames,
2051 : resources, section_opts, show_opts, false);
2052 : }
2053 :
2054 : /* If there were any failed actions, print them */
2055 0 : if (pcmk_is_set(section_opts, pcmk_section_failures)
2056 0 : && (scheduler->failed != NULL)
2057 0 : && (scheduler->failed->children != NULL)) {
2058 :
2059 0 : out->message(out, "failed-action-list", scheduler, unames, resources,
2060 : show_opts, false);
2061 : }
2062 :
2063 : /* Print failed stonith actions */
2064 0 : if (pcmk_is_set(section_opts, pcmk_section_fence_failed) &&
2065 : fence_history != pcmk__fence_history_none) {
2066 0 : if (history_rc == 0) {
2067 0 : stonith_history_t *hp = NULL;
2068 :
2069 0 : hp = stonith__first_matching_event(stonith_history,
2070 : stonith__event_state_eq,
2071 : GINT_TO_POINTER(st_failed));
2072 0 : if (hp) {
2073 0 : out->message(out, "failed-fencing-list", stonith_history,
2074 : unames, section_opts, show_opts, false);
2075 : }
2076 : } else {
2077 0 : out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
2078 0 : out->list_item(out, NULL, "Failed to get fencing history: %s",
2079 : crm_exit_str(history_rc));
2080 0 : out->end_list(out);
2081 : }
2082 : }
2083 :
2084 : /* Print stonith history */
2085 0 : if (pcmk_any_flags_set(section_opts, pcmk_section_fencing_all) &&
2086 : fence_history != pcmk__fence_history_none) {
2087 0 : if (history_rc != 0) {
2088 0 : if (!already_printed_failure) {
2089 0 : out->begin_list(out, NULL, NULL, "Failed Fencing Actions");
2090 0 : out->list_item(out, NULL, "Failed to get fencing history: %s",
2091 : crm_exit_str(history_rc));
2092 0 : out->end_list(out);
2093 : }
2094 0 : } else if (pcmk_is_set(section_opts, pcmk_section_fence_worked)) {
2095 0 : stonith_history_t *hp = NULL;
2096 :
2097 0 : hp = stonith__first_matching_event(stonith_history,
2098 : stonith__event_state_neq,
2099 : GINT_TO_POINTER(st_failed));
2100 0 : if (hp) {
2101 0 : out->message(out, "fencing-list", hp, unames, section_opts,
2102 : show_opts, false);
2103 : }
2104 0 : } else if (pcmk_is_set(section_opts, pcmk_section_fence_pending)) {
2105 0 : stonith_history_t *hp = NULL;
2106 :
2107 0 : hp = stonith__first_matching_event(stonith_history,
2108 : stonith__event_state_pending,
2109 : NULL);
2110 0 : if (hp) {
2111 0 : out->message(out, "pending-fencing-list", hp, unames,
2112 : section_opts, show_opts, false);
2113 : }
2114 : }
2115 : }
2116 :
2117 : /* Print tickets if requested */
2118 0 : if (pcmk_is_set(section_opts, pcmk_section_tickets)) {
2119 0 : out->message(out, "ticket-list", scheduler->tickets, false, false, false);
2120 : }
2121 :
2122 : /* Print negative location constraints if requested */
2123 0 : if (pcmk_is_set(section_opts, pcmk_section_bans)) {
2124 0 : out->message(out, "ban-list", scheduler, prefix, resources, show_opts,
2125 : false);
2126 : }
2127 :
2128 0 : return pcmk_rc_ok;
2129 : }
2130 :
2131 : #define KV_PAIR(k, v) do { \
2132 : if (legacy) { \
2133 : pcmk__g_strcat(s, k "=", pcmk__s(v, ""), " ", NULL); \
2134 : } else { \
2135 : pcmk__g_strcat(s, k "=\"", pcmk__s(v, ""), "\"", NULL); \
2136 : } \
2137 : } while (0)
2138 :
2139 : PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *",
2140 : "const char *", "const char *", "bool", "bool")
2141 : static int
2142 0 : attribute_default(pcmk__output_t *out, va_list args)
2143 : {
2144 0 : const char *scope = va_arg(args, const char *);
2145 0 : const char *instance = va_arg(args, const char *);
2146 0 : const char *name = va_arg(args, const char *);
2147 0 : const char *value = va_arg(args, const char *);
2148 0 : const char *host = va_arg(args, const char *);
2149 0 : bool quiet = va_arg(args, int);
2150 0 : bool legacy = va_arg(args, int);
2151 :
2152 0 : gchar *value_esc = NULL;
2153 0 : GString *s = NULL;
2154 :
2155 0 : if (quiet) {
2156 0 : if (value != NULL) {
2157 : /* Quiet needs to be turned off for ->info() to do anything */
2158 0 : bool was_quiet = out->is_quiet(out);
2159 :
2160 0 : if (was_quiet) {
2161 0 : out->quiet = false;
2162 : }
2163 :
2164 0 : out->info(out, "%s", value);
2165 :
2166 0 : out->quiet = was_quiet;
2167 : }
2168 :
2169 0 : return pcmk_rc_ok;
2170 : }
2171 :
2172 0 : s = g_string_sized_new(50);
2173 :
2174 0 : if (pcmk__xml_needs_escape(value, pcmk__xml_escape_attr_pretty)) {
2175 0 : value_esc = pcmk__xml_escape(value, pcmk__xml_escape_attr_pretty);
2176 0 : value = value_esc;
2177 : }
2178 :
2179 0 : if (!pcmk__str_empty(scope)) {
2180 0 : KV_PAIR(PCMK_XA_SCOPE, scope);
2181 : }
2182 :
2183 0 : if (!pcmk__str_empty(instance)) {
2184 0 : KV_PAIR(PCMK_XA_ID, instance);
2185 : }
2186 :
2187 0 : KV_PAIR(PCMK_XA_NAME, name);
2188 :
2189 0 : if (!pcmk__str_empty(host)) {
2190 0 : KV_PAIR(PCMK_XA_HOST, host);
2191 : }
2192 :
2193 0 : if (legacy) {
2194 0 : pcmk__g_strcat(s, PCMK_XA_VALUE "=", pcmk__s(value, "(null)"), NULL);
2195 : } else {
2196 0 : pcmk__g_strcat(s, PCMK_XA_VALUE "=\"", pcmk__s(value, ""), "\"", NULL);
2197 : }
2198 :
2199 0 : out->info(out, "%s", s->str);
2200 :
2201 0 : g_free(value_esc);
2202 0 : g_string_free(s, TRUE);
2203 0 : return pcmk_rc_ok;
2204 : }
2205 :
2206 : PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *",
2207 : "const char *", "const char *", "bool", "bool")
2208 : static int
2209 0 : attribute_xml(pcmk__output_t *out, va_list args)
2210 : {
2211 0 : const char *scope = va_arg(args, const char *);
2212 0 : const char *instance = va_arg(args, const char *);
2213 0 : const char *name = va_arg(args, const char *);
2214 0 : const char *value = va_arg(args, const char *);
2215 0 : const char *host = va_arg(args, const char *);
2216 0 : bool quiet G_GNUC_UNUSED = va_arg(args, int);
2217 0 : bool legacy G_GNUC_UNUSED = va_arg(args, int);
2218 :
2219 0 : xmlNodePtr node = NULL;
2220 :
2221 0 : node = pcmk__output_create_xml_node(out, PCMK_XE_ATTRIBUTE,
2222 : PCMK_XA_NAME, name,
2223 : PCMK_XA_VALUE, pcmk__s(value, ""),
2224 : NULL);
2225 :
2226 0 : if (!pcmk__str_empty(scope)) {
2227 0 : crm_xml_add(node, PCMK_XA_SCOPE, scope);
2228 : }
2229 :
2230 0 : if (!pcmk__str_empty(instance)) {
2231 0 : crm_xml_add(node, PCMK_XA_ID, instance);
2232 : }
2233 :
2234 0 : if (!pcmk__str_empty(host)) {
2235 0 : crm_xml_add(node, PCMK_XA_HOST, host);
2236 : }
2237 :
2238 0 : return pcmk_rc_ok;
2239 : }
2240 :
2241 : PCMK__OUTPUT_ARGS("rule-check", "const char *", "int", "const char *")
2242 : static int
2243 0 : rule_check_default(pcmk__output_t *out, va_list args)
2244 : {
2245 0 : const char *rule_id = va_arg(args, const char *);
2246 0 : int result = va_arg(args, int);
2247 0 : const char *error = va_arg(args, const char *);
2248 :
2249 0 : switch (result) {
2250 0 : case pcmk_rc_within_range:
2251 0 : return out->info(out, "Rule %s is still in effect", rule_id);
2252 0 : case pcmk_rc_ok:
2253 0 : return out->info(out, "Rule %s satisfies conditions", rule_id);
2254 0 : case pcmk_rc_after_range:
2255 0 : return out->info(out, "Rule %s is expired", rule_id);
2256 0 : case pcmk_rc_before_range:
2257 0 : return out->info(out, "Rule %s has not yet taken effect", rule_id);
2258 0 : case pcmk_rc_op_unsatisfied:
2259 0 : return out->info(out, "Rule %s does not satisfy conditions",
2260 : rule_id);
2261 0 : default:
2262 0 : out->err(out,
2263 : "Could not determine whether rule %s is in effect: %s",
2264 : rule_id, ((error != NULL)? error : "unexpected error"));
2265 0 : return pcmk_rc_ok;
2266 : }
2267 : }
2268 :
2269 : PCMK__OUTPUT_ARGS("rule-check", "const char *", "int", "const char *")
2270 : static int
2271 0 : rule_check_xml(pcmk__output_t *out, va_list args)
2272 : {
2273 0 : const char *rule_id = va_arg(args, const char *);
2274 0 : int result = va_arg(args, int);
2275 0 : const char *error = va_arg(args, const char *);
2276 :
2277 0 : char *rc_str = pcmk__itoa(pcmk_rc2exitc(result));
2278 :
2279 0 : pcmk__output_create_xml_node(out, PCMK_XE_RULE_CHECK,
2280 : PCMK_XA_RULE_ID, rule_id,
2281 : PCMK_XA_RC, rc_str,
2282 : NULL);
2283 0 : free(rc_str);
2284 :
2285 0 : switch (result) {
2286 0 : case pcmk_rc_within_range:
2287 : case pcmk_rc_ok:
2288 : case pcmk_rc_after_range:
2289 : case pcmk_rc_before_range:
2290 : case pcmk_rc_op_unsatisfied:
2291 0 : return pcmk_rc_ok;
2292 0 : default:
2293 0 : out->err(out,
2294 : "Could not determine whether rule %s is in effect: %s",
2295 : rule_id, ((error != NULL)? error : "unexpected error"));
2296 0 : return pcmk_rc_ok;
2297 : }
2298 : }
2299 :
2300 : PCMK__OUTPUT_ARGS("result-code", "int", "const char *", "const char *")
2301 : static int
2302 0 : result_code_none(pcmk__output_t *out, va_list args)
2303 : {
2304 0 : return pcmk_rc_no_output;
2305 : }
2306 :
2307 : PCMK__OUTPUT_ARGS("result-code", "int", "const char *", "const char *")
2308 : static int
2309 0 : result_code_text(pcmk__output_t *out, va_list args)
2310 : {
2311 0 : int code = va_arg(args, int);
2312 0 : const char *name = va_arg(args, const char *);
2313 0 : const char *desc = va_arg(args, const char *);
2314 :
2315 : static int code_width = 0;
2316 :
2317 0 : if (out->is_quiet(out)) {
2318 : /* If out->is_quiet(), don't print the code. Print name and/or desc in a
2319 : * compact format for text output, or print nothing at all for none-type
2320 : * output.
2321 : */
2322 0 : if ((name != NULL) && (desc != NULL)) {
2323 0 : pcmk__formatted_printf(out, "%s - %s\n", name, desc);
2324 :
2325 0 : } else if ((name != NULL) || (desc != NULL)) {
2326 0 : pcmk__formatted_printf(out, "%s\n", ((name != NULL)? name : desc));
2327 : }
2328 0 : return pcmk_rc_ok;
2329 : }
2330 :
2331 : /* Get length of longest (most negative) standard Pacemaker return code
2332 : * This should be longer than all the values of any other type of return
2333 : * code.
2334 : */
2335 0 : if (code_width == 0) {
2336 0 : long long most_negative = pcmk_rc_error - (long long) pcmk__n_rc + 1;
2337 0 : code_width = (int) snprintf(NULL, 0, "%lld", most_negative);
2338 : }
2339 :
2340 0 : if ((name != NULL) && (desc != NULL)) {
2341 : static int name_width = 0;
2342 :
2343 0 : if (name_width == 0) {
2344 : // Get length of longest standard Pacemaker return code name
2345 0 : for (int lpc = 0; lpc < pcmk__n_rc; lpc++) {
2346 0 : int len = (int) strlen(pcmk_rc_name(pcmk_rc_error - lpc));
2347 0 : name_width = QB_MAX(name_width, len);
2348 : }
2349 : }
2350 0 : return out->info(out, "% *d: %-*s %s", code_width, code, name_width,
2351 : name, desc);
2352 : }
2353 :
2354 0 : if ((name != NULL) || (desc != NULL)) {
2355 0 : return out->info(out, "% *d: %s", code_width, code,
2356 : ((name != NULL)? name : desc));
2357 : }
2358 :
2359 0 : return out->info(out, "% *d", code_width, code);
2360 : }
2361 :
2362 : PCMK__OUTPUT_ARGS("result-code", "int", "const char *", "const char *")
2363 : static int
2364 0 : result_code_xml(pcmk__output_t *out, va_list args)
2365 : {
2366 0 : int code = va_arg(args, int);
2367 0 : const char *name = va_arg(args, const char *);
2368 0 : const char *desc = va_arg(args, const char *);
2369 :
2370 0 : char *code_str = pcmk__itoa(code);
2371 :
2372 0 : pcmk__output_create_xml_node(out, PCMK_XE_RESULT_CODE,
2373 : PCMK_XA_CODE, code_str,
2374 : PCMK_XA_NAME, name,
2375 : PCMK_XA_DESCRIPTION, desc,
2376 : NULL);
2377 0 : free(code_str);
2378 0 : return pcmk_rc_ok;
2379 : }
2380 :
2381 : PCMK__OUTPUT_ARGS("ticket-attribute", "const char *", "const char *", "const char *")
2382 : static int
2383 0 : ticket_attribute_default(pcmk__output_t *out, va_list args)
2384 : {
2385 0 : const char *ticket_id G_GNUC_UNUSED = va_arg(args, const char *);
2386 0 : const char *name G_GNUC_UNUSED = va_arg(args, const char *);
2387 0 : const char *value = va_arg(args, const char *);
2388 :
2389 0 : out->info(out, "%s", value);
2390 0 : return pcmk_rc_ok;
2391 : }
2392 :
2393 : PCMK__OUTPUT_ARGS("ticket-attribute", "const char *", "const char *", "const char *")
2394 : static int
2395 0 : ticket_attribute_xml(pcmk__output_t *out, va_list args)
2396 : {
2397 0 : const char *ticket_id = va_arg(args, const char *);
2398 0 : const char *name = va_arg(args, const char *);
2399 0 : const char *value = va_arg(args, const char *);
2400 :
2401 : /* Create:
2402 : * <tickets>
2403 : * <ticket id="">
2404 : * <attribute name="" value="" />
2405 : * </ticket>
2406 : * </tickets>
2407 : */
2408 0 : pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL);
2409 0 : pcmk__output_xml_create_parent(out, PCMK_XE_TICKET,
2410 : PCMK_XA_ID, ticket_id, NULL);
2411 0 : pcmk__output_create_xml_node(out, PCMK_XA_ATTRIBUTE,
2412 : PCMK_XA_NAME, name,
2413 : PCMK_XA_VALUE, value,
2414 : NULL);
2415 0 : pcmk__output_xml_pop_parent(out);
2416 0 : pcmk__output_xml_pop_parent(out);
2417 :
2418 0 : return pcmk_rc_ok;
2419 : }
2420 :
2421 : PCMK__OUTPUT_ARGS("ticket-constraints", "xmlNode *")
2422 : static int
2423 0 : ticket_constraints_default(pcmk__output_t *out, va_list args)
2424 : {
2425 0 : xmlNode *constraint_xml = va_arg(args, xmlNode *);
2426 :
2427 : /* constraint_xml can take two forms:
2428 : *
2429 : * <rsc_ticket id="rsc1-req-ticketA" rsc="rsc1" ticket="ticketA" ... />
2430 : *
2431 : * for when there's only one ticket in the CIB, or when the user asked
2432 : * for a specific ticket (crm_ticket -c -t for instance)
2433 : *
2434 : * <xpath-query>
2435 : * <rsc_ticket id="rsc1-req-ticketA" rsc="rsc1" ticket="ticketA" ... />
2436 : * <rsc_ticket id="rsc1-req-ticketB" rsc="rsc2" ticket="ticketB" ... />
2437 : * </xpath-query>
2438 : *
2439 : * for when there's multiple tickets in the and the user did not ask for
2440 : * a specific one.
2441 : *
2442 : * In both cases, we simply output a <rsc_ticket> element for each ticket
2443 : * in the results.
2444 : */
2445 0 : out->info(out, "Constraints XML:\n");
2446 :
2447 0 : if (pcmk__xe_is(constraint_xml, PCMK__XE_XPATH_QUERY)) {
2448 0 : xmlNode *child = pcmk__xe_first_child(constraint_xml, NULL, NULL, NULL);
2449 :
2450 : do {
2451 0 : GString *buf = g_string_sized_new(1024);
2452 :
2453 0 : pcmk__xml_string(child, pcmk__xml_fmt_pretty, buf, 0);
2454 0 : out->output_xml(out, PCMK_XE_CONSTRAINT, buf->str);
2455 0 : g_string_free(buf, TRUE);
2456 :
2457 0 : child = pcmk__xe_next(child);
2458 0 : } while (child != NULL);
2459 : } else {
2460 0 : GString *buf = g_string_sized_new(1024);
2461 :
2462 0 : pcmk__xml_string(constraint_xml, pcmk__xml_fmt_pretty, buf, 0);
2463 0 : out->output_xml(out, PCMK_XE_CONSTRAINT, buf->str);
2464 0 : g_string_free(buf, TRUE);
2465 : }
2466 :
2467 0 : return pcmk_rc_ok;
2468 : }
2469 :
2470 : static int
2471 0 : add_ticket_element_with_constraints(xmlNode *node, void *userdata)
2472 : {
2473 0 : pcmk__output_t *out = (pcmk__output_t *) userdata;
2474 0 : const char *ticket_id = crm_element_value(node, PCMK_XA_TICKET);
2475 :
2476 0 : pcmk__output_xml_create_parent(out, PCMK_XE_TICKET,
2477 : PCMK_XA_ID, ticket_id, NULL);
2478 0 : pcmk__output_xml_create_parent(out, PCMK_XE_CONSTRAINTS, NULL);
2479 0 : pcmk__output_xml_add_node_copy(out, node);
2480 :
2481 : /* Pop two parents so now we are back under the <tickets> element */
2482 0 : pcmk__output_xml_pop_parent(out);
2483 0 : pcmk__output_xml_pop_parent(out);
2484 :
2485 0 : return pcmk_rc_ok;
2486 : }
2487 :
2488 : static int
2489 0 : add_resource_element(xmlNode *node, void *userdata)
2490 : {
2491 0 : pcmk__output_t *out = (pcmk__output_t *) userdata;
2492 0 : const char *rsc = crm_element_value(node, PCMK_XA_RSC);
2493 :
2494 0 : pcmk__output_create_xml_node(out, PCMK_XE_RESOURCE,
2495 : PCMK_XA_ID, rsc, NULL);
2496 0 : return pcmk_rc_ok;
2497 : }
2498 :
2499 : PCMK__OUTPUT_ARGS("ticket-constraints", "xmlNode *")
2500 : static int
2501 0 : ticket_constraints_xml(pcmk__output_t *out, va_list args)
2502 : {
2503 0 : xmlNode *constraint_xml = va_arg(args, xmlNode *);
2504 :
2505 : /* Create:
2506 : * <tickets>
2507 : * <ticket id="">
2508 : * <constraints>
2509 : * <rsc_ticket />
2510 : * </constraints>
2511 : * </ticket>
2512 : * ...
2513 : * </tickets>
2514 : */
2515 0 : pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL);
2516 :
2517 0 : if (pcmk__xe_is(constraint_xml, PCMK__XE_XPATH_QUERY)) {
2518 : /* Iterate through the list of children once to create all the
2519 : * ticket/constraint elements.
2520 : */
2521 0 : pcmk__xe_foreach_child(constraint_xml, NULL, add_ticket_element_with_constraints, out);
2522 :
2523 : /* Put us back at the same level as where <tickets> was created. */
2524 0 : pcmk__output_xml_pop_parent(out);
2525 :
2526 : /* Constraints can reference a resource ID that is defined in the XML
2527 : * schema as an IDREF. This requires some other element to be present
2528 : * with an id= attribute that matches.
2529 : *
2530 : * Iterate through the list of children a second time to create the
2531 : * following:
2532 : *
2533 : * <resources>
2534 : * <resource id="" />
2535 : * ...
2536 : * </resources>
2537 : */
2538 0 : pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL);
2539 0 : pcmk__xe_foreach_child(constraint_xml, NULL, add_resource_element, out);
2540 0 : pcmk__output_xml_pop_parent(out);
2541 :
2542 : } else {
2543 : /* Creating the output for a single constraint is much easier. All the
2544 : * comments in the above block apply here.
2545 : */
2546 0 : add_ticket_element_with_constraints(constraint_xml, out);
2547 0 : pcmk__output_xml_pop_parent(out);
2548 :
2549 0 : pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL);
2550 0 : add_resource_element(constraint_xml, out);
2551 0 : pcmk__output_xml_pop_parent(out);
2552 : }
2553 :
2554 0 : return pcmk_rc_ok;
2555 : }
2556 :
2557 : PCMK__OUTPUT_ARGS("ticket-state", "xmlNode *")
2558 : static int
2559 0 : ticket_state_default(pcmk__output_t *out, va_list args)
2560 : {
2561 0 : xmlNode *state_xml = va_arg(args, xmlNode *);
2562 :
2563 0 : GString *buf = g_string_sized_new(1024);
2564 :
2565 0 : out->info(out, "State XML:\n");
2566 0 : pcmk__xml_string(state_xml, pcmk__xml_fmt_pretty, buf, 0);
2567 0 : out->output_xml(out, PCMK__XE_TICKET_STATE, buf->str);
2568 :
2569 0 : g_string_free(buf, TRUE);
2570 0 : return pcmk_rc_ok;
2571 : }
2572 :
2573 : static int
2574 0 : add_ticket_element(xmlNode *node, void *userdata)
2575 : {
2576 0 : pcmk__output_t *out = (pcmk__output_t *) userdata;
2577 0 : xmlNode *ticket_node = NULL;
2578 :
2579 0 : ticket_node = pcmk__output_create_xml_node(out, PCMK_XE_TICKET, NULL);
2580 0 : pcmk__xe_copy_attrs(ticket_node, node, pcmk__xaf_none);
2581 0 : return pcmk_rc_ok;
2582 : }
2583 :
2584 : PCMK__OUTPUT_ARGS("ticket-state", "xmlNode *")
2585 : static int
2586 0 : ticket_state_xml(pcmk__output_t *out, va_list args)
2587 : {
2588 0 : xmlNode *state_xml = va_arg(args, xmlNode *);
2589 :
2590 : /* Create:
2591 : * <tickets>
2592 : * <ticket />
2593 : * ...
2594 : * </tickets>
2595 : */
2596 0 : pcmk__output_xml_create_parent(out, PCMK_XE_TICKETS, NULL);
2597 :
2598 0 : if (state_xml->children != NULL) {
2599 : /* Iterate through the list of children once to create all the
2600 : * ticket elements.
2601 : */
2602 0 : pcmk__xe_foreach_child(state_xml, PCMK__XE_TICKET_STATE, add_ticket_element, out);
2603 :
2604 : } else {
2605 0 : add_ticket_element(state_xml, out);
2606 : }
2607 :
2608 0 : pcmk__output_xml_pop_parent(out);
2609 0 : return pcmk_rc_ok;
2610 : }
2611 :
2612 : static pcmk__message_entry_t fmt_functions[] = {
2613 : { "attribute", "default", attribute_default },
2614 : { "attribute", "xml", attribute_xml },
2615 : { "cluster-status", "default", pcmk__cluster_status_text },
2616 : { "cluster-status", "html", cluster_status_html },
2617 : { "cluster-status", "xml", cluster_status_xml },
2618 : { "crmadmin-node", "default", crmadmin_node },
2619 : { "crmadmin-node", "text", crmadmin_node_text },
2620 : { "crmadmin-node", "xml", crmadmin_node_xml },
2621 : { "dc", "default", dc },
2622 : { "dc", "text", dc_text },
2623 : { "dc", "xml", dc_xml },
2624 : { "digests", "default", digests_text },
2625 : { "digests", "xml", digests_xml },
2626 : { "health", "default", health },
2627 : { "health", "text", health_text },
2628 : { "health", "xml", health_xml },
2629 : { "inject-attr", "default", inject_attr },
2630 : { "inject-attr", "xml", inject_attr_xml },
2631 : { "inject-cluster-action", "default", inject_cluster_action },
2632 : { "inject-cluster-action", "xml", inject_cluster_action_xml },
2633 : { "inject-fencing-action", "default", inject_fencing_action },
2634 : { "inject-fencing-action", "xml", inject_fencing_action_xml },
2635 : { "inject-modify-config", "default", inject_modify_config },
2636 : { "inject-modify-config", "xml", inject_modify_config_xml },
2637 : { "inject-modify-node", "default", inject_modify_node },
2638 : { "inject-modify-node", "xml", inject_modify_node_xml },
2639 : { "inject-modify-ticket", "default", inject_modify_ticket },
2640 : { "inject-modify-ticket", "xml", inject_modify_ticket_xml },
2641 : { "inject-pseudo-action", "default", inject_pseudo_action },
2642 : { "inject-pseudo-action", "xml", inject_pseudo_action_xml },
2643 : { "inject-rsc-action", "default", inject_rsc_action },
2644 : { "inject-rsc-action", "xml", inject_rsc_action_xml },
2645 : { "inject-spec", "default", inject_spec },
2646 : { "inject-spec", "xml", inject_spec_xml },
2647 : { "locations-and-colocations", "default", locations_and_colocations },
2648 : { "locations-and-colocations", "xml", locations_and_colocations_xml },
2649 : { "locations-list", "default", locations_list },
2650 : { "locations-list", "xml", locations_list_xml },
2651 : { "node-action", "default", node_action },
2652 : { "node-action", "xml", node_action_xml },
2653 : { "node-info", "default", node_info_default },
2654 : { "node-info", "xml", node_info_xml },
2655 : { "pacemakerd-health", "default", pacemakerd_health },
2656 : { "pacemakerd-health", "html", pacemakerd_health_html },
2657 : { "pacemakerd-health", "text", pacemakerd_health_text },
2658 : { "pacemakerd-health", "xml", pacemakerd_health_xml },
2659 : { "profile", "default", profile_default, },
2660 : { "profile", "xml", profile_xml },
2661 : { "result-code", PCMK_VALUE_NONE, result_code_none },
2662 : { "result-code", "text", result_code_text },
2663 : { "result-code", "xml", result_code_xml },
2664 : { "rsc-action", "default", rsc_action_default },
2665 : { "rsc-action-item", "default", rsc_action_item },
2666 : { "rsc-action-item", "xml", rsc_action_item_xml },
2667 : { "rsc-is-colocated-with-list", "default", rsc_is_colocated_with_list },
2668 : { "rsc-is-colocated-with-list", "xml", rsc_is_colocated_with_list_xml },
2669 : { "rscs-colocated-with-list", "default", rscs_colocated_with_list },
2670 : { "rscs-colocated-with-list", "xml", rscs_colocated_with_list_xml },
2671 : { "rule-check", "default", rule_check_default },
2672 : { "rule-check", "xml", rule_check_xml },
2673 : { "ticket-attribute", "default", ticket_attribute_default },
2674 : { "ticket-attribute", "xml", ticket_attribute_xml },
2675 : { "ticket-constraints", "default", ticket_constraints_default },
2676 : { "ticket-constraints", "xml", ticket_constraints_xml },
2677 : { "ticket-state", "default", ticket_state_default },
2678 : { "ticket-state", "xml", ticket_state_xml },
2679 :
2680 : { NULL, NULL, NULL }
2681 : };
2682 :
2683 : void
2684 0 : pcmk__register_lib_messages(pcmk__output_t *out) {
2685 0 : pcmk__register_messages(out, fmt_functions);
2686 0 : }
|