pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
pcmk_ticket.c
Go to the documentation of this file.
1 /*
2  * Copyright 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 
12 #include <crm/cib/internal.h>
13 #include <crm/pengine/internal.h>
14 
15 #include <pacemaker.h>
16 #include <pacemaker-internal.h>
17 
18 #include "libpacemaker_private.h"
19 
20 static int
21 build_ticket_modify_xml(cib_t *cib, const char *ticket_id, xmlNode **ticket_state_xml,
22  xmlNode **xml_top)
23 {
24  int rc = pcmk__get_ticket_state(cib, ticket_id, ticket_state_xml);
25 
26  if (rc == pcmk_rc_ok || rc == pcmk_rc_duplicate_id) {
27  /* Ticket(s) found - return their state */
28  *xml_top = *ticket_state_xml;
29 
30  } else if (rc == ENXIO) {
31  /* No ticket found - build the XML needed to create it */
32  xmlNode *xml_obj = NULL;
33 
34  *xml_top = pcmk__xe_create(NULL, PCMK_XE_STATUS);
35  xml_obj = pcmk__xe_create(*xml_top, PCMK_XE_TICKETS);
36  *ticket_state_xml = pcmk__xe_create(xml_obj, PCMK__XE_TICKET_STATE);
37  crm_xml_add(*ticket_state_xml, PCMK_XA_ID, ticket_id);
38 
39  rc = pcmk_rc_ok;
40 
41  } else {
42  /* Some other error occurred - clean up and return */
43  free_xml(*ticket_state_xml);
44  }
45 
46  return rc;
47 }
48 
49 static void
50 add_attribute_xml(pcmk_scheduler_t *scheduler, const char *ticket_id,
51  GHashTable *attr_set, xmlNode **ticket_state_xml)
52 {
53  GHashTableIter hash_iter;
54  char *key = NULL;
55  char *value = NULL;
56 
57  pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
58 
59  g_hash_table_iter_init(&hash_iter, attr_set);
60  while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) {
61  crm_xml_add(*ticket_state_xml, key, value);
62 
63  if (pcmk__str_eq(key, PCMK__XA_GRANTED, pcmk__str_none)
64  && (ticket == NULL || ticket->granted == FALSE)
65  && crm_is_true(value)) {
66 
67  char *now = pcmk__ttoa(time(NULL));
68 
69  crm_xml_add(*ticket_state_xml, PCMK_XA_LAST_GRANTED, now);
70  free(now);
71  }
72  }
73 }
74 
75 int
76 pcmk__get_ticket_state(cib_t *cib, const char *ticket_id, xmlNode **state)
77 {
78  int rc = pcmk_rc_ok;
79  xmlNode *xml_search = NULL;
80  char *xpath = NULL;
81 
82  pcmk__assert((cib != NULL) && (state != NULL));
83  *state = NULL;
84 
85  if (ticket_id != NULL) {
87  "/" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"%s\"]",
88  ticket_id);
89  } else {
91  }
92 
93  rc = cib->cmds->query(cib, xpath, &xml_search,
95  rc = pcmk_legacy2rc(rc);
96 
97  if (rc == pcmk_rc_ok) {
98  crm_log_xml_debug(xml_search, "Match");
99 
100  if (xml_search->children != NULL && ticket_id != NULL) {
102  }
103  }
104 
105  free(xpath);
106 
107  *state = xml_search;
108  return rc;
109 }
110 
111 int
112 pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
113 {
114  int rc = pcmk_rc_ok;
115  xmlNode *result = NULL;
116  const char *xpath_base = NULL;
117  char *xpath = NULL;
118 
119  pcmk__assert((out != NULL) && (cib != NULL));
120 
122  pcmk__assert(xpath_base != NULL);
123 
124  if (ticket_id != NULL) {
125  xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET "[@" PCMK_XA_TICKET "=\"%s\"]",
126  xpath_base, ticket_id);
127  } else {
128  xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET, xpath_base);
129  }
130 
131  rc = cib->cmds->query(cib, (const char *) xpath, &result,
133  rc = pcmk_legacy2rc(rc);
134 
135  if (result != NULL) {
136  out->message(out, "ticket-constraints", result);
137  free_xml(result);
138  }
139 
140  free(xpath);
141  return rc;
142 }
143 
144 int
145 pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id)
146 {
147  pcmk__output_t *out = NULL;
148  int rc = pcmk_rc_ok;
149  cib_t *cib = NULL;
150 
151  rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml);
152  if (rc != pcmk_rc_ok) {
153  goto done;
154  }
155 
156  rc = pcmk__ticket_constraints(out, cib, ticket_id);
157 
158 done:
159  if (cib != NULL) {
161  }
162 
163  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
164  return rc;
165 }
166 
167 static int
168 delete_single_ticket(xmlNode *child, void *userdata)
169 {
170  int rc = pcmk_rc_ok;
171  cib_t *cib = (cib_t *) userdata;
172 
173  rc = cib->cmds->remove(cib, PCMK_XE_STATUS, child, cib_sync_call);
174  rc = pcmk_legacy2rc(rc);
175 
176  return rc;
177 }
178 
179 int
181  const char *ticket_id, bool force)
182 {
183  int rc = pcmk_rc_ok;
184  xmlNode *state = NULL;
185 
186  pcmk__assert((cib != NULL) && (scheduler != NULL));
187 
188  if (ticket_id == NULL) {
189  return EINVAL;
190  }
191 
192  if (!force) {
193  pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
194 
195  if (ticket == NULL) {
196  return ENXIO;
197  }
198 
199  if (ticket->granted) {
200  return EACCES;
201  }
202  }
203 
204  rc = pcmk__get_ticket_state(cib, ticket_id, &state);
205 
206  if (rc == pcmk_rc_duplicate_id) {
207  out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s",
208  ticket_id);
209 
210  } else if (rc == ENXIO) {
211  return pcmk_rc_ok;
212 
213  } else if (rc != pcmk_rc_ok) {
214  return rc;
215  }
216 
217  crm_log_xml_debug(state, "Delete");
218 
219  if (rc == pcmk_rc_duplicate_id) {
220  rc = pcmk__xe_foreach_child(state, NULL, delete_single_ticket, cib);
221  } else {
222  rc = delete_single_ticket(state, cib);
223  }
224 
225  if (rc == pcmk_rc_ok) {
226  out->info(out, "Cleaned up %s", ticket_id);
227  }
228 
229  free_xml(state);
230  return rc;
231 }
232 
233 int
234 pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force)
235 {
236  pcmk_scheduler_t *scheduler = NULL;
237  pcmk__output_t *out = NULL;
238  cib_t *cib = NULL;
239  int rc = pcmk_rc_ok;
240 
241  rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
242  if (rc != pcmk_rc_ok) {
243  goto done;
244  }
245 
246  rc = pcmk__ticket_delete(out, cib, scheduler, ticket_id, force);
247 
248 done:
249  if (cib != NULL) {
251  }
252 
253  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
255  return rc;
256 }
257 
258 int
260  const char *ticket_id, const char *attr_name,
261  const char *attr_default)
262 {
263  int rc = pcmk_rc_ok;
264  const char *attr_value = NULL;
265  pcmk_ticket_t *ticket = NULL;
266 
267  pcmk__assert(out != NULL && scheduler != NULL);
268 
269  if (ticket_id == NULL || attr_name == NULL) {
270  return EINVAL;
271  }
272 
273  ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
274 
275  if (ticket != NULL) {
276  attr_value = g_hash_table_lookup(ticket->state, attr_name);
277  }
278 
279  if (attr_value != NULL) {
280  out->message(out, "ticket-attribute", ticket_id, attr_name, attr_value);
281  } else if (attr_default != NULL) {
282  out->message(out, "ticket-attribute", ticket_id, attr_name, attr_default);
283  } else {
284  rc = ENXIO;
285  }
286 
287  return rc;
288 }
289 
290 int
291 pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id,
292  const char *attr_name, const char *attr_default)
293 {
294  pcmk_scheduler_t *scheduler = NULL;
295  pcmk__output_t *out = NULL;
296  int rc = pcmk_rc_ok;
297 
298  rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml);
299  if (rc != pcmk_rc_ok) {
300  goto done;
301  }
302 
303  rc = pcmk__ticket_get_attr(out, scheduler, ticket_id, attr_name, attr_default);
304 
305 done:
306  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
308  return rc;
309 }
310 
311 int
313  const char *ticket_id, bool details, bool raw)
314 {
315  int rc = pcmk_rc_ok;
316 
317  pcmk__assert((out != NULL) && (scheduler != NULL));
318 
319  if (ticket_id != NULL) {
320  GHashTable *tickets = NULL;
321  pcmk_ticket_t *ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
322 
323  if (ticket == NULL) {
324  return ENXIO;
325  }
326 
327  /* The ticket-list message expects a GHashTable, so we'll construct
328  * one with just this single item.
329  */
330  tickets = pcmk__strkey_table(free, NULL);
331  g_hash_table_insert(tickets, strdup(ticket->id), ticket);
332  out->message(out, "ticket-list", tickets, false, raw, details);
333  g_hash_table_destroy(tickets);
334 
335  } else {
336  out->message(out, "ticket-list", scheduler->tickets, false, raw, details);
337  }
338 
339  return rc;
340 }
341 
342 int
343 pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id)
344 {
345  pcmk_scheduler_t *scheduler = NULL;
346  pcmk__output_t *out = NULL;
347  int rc = pcmk_rc_ok;
348 
349  rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml);
350  if (rc != pcmk_rc_ok) {
351  goto done;
352  }
353 
355 
356  /* XML output (which is the only format supported by public API functions
357  * due to the use of pcmk__xml_output_new above) always prints all details,
358  * so just pass false for the last two arguments.
359  */
360  rc = pcmk__ticket_info(out, scheduler, ticket_id, false, false);
361 
362 done:
363  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
365  return rc;
366 }
367 
368 int
370  const char *ticket_id, GList *attr_delete, bool force)
371 {
372  xmlNode *ticket_state_xml = NULL;
373  xmlNode *xml_top = NULL;
374  int rc = pcmk_rc_ok;
375 
376  pcmk__assert((out != NULL) && (cib != NULL) && (scheduler != NULL));
377 
378  if (ticket_id == NULL) {
379  return EINVAL;
380  }
381 
382  /* Nothing to do */
383  if (attr_delete == NULL) {
384  return pcmk_rc_ok;
385  }
386 
387  rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top);
388 
389  if (rc == pcmk_rc_duplicate_id) {
390  out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id);
391  } else if (rc != pcmk_rc_ok) {
392  free_xml(ticket_state_xml);
393  return rc;
394  }
395 
396  for (GList *list_iter = attr_delete; list_iter != NULL; list_iter = list_iter->next) {
397  const char *key = list_iter->data;
398 
399  if (!force && pcmk__str_eq(key, PCMK__XA_GRANTED, pcmk__str_none)) {
400  free_xml(ticket_state_xml);
401  return EACCES;
402  }
403 
404  pcmk__xe_remove_attr(ticket_state_xml, key);
405  }
406 
407  crm_log_xml_debug(xml_top, "Replace");
408  rc = cib->cmds->replace(cib, PCMK_XE_STATUS, ticket_state_xml, cib_sync_call);
409  rc = pcmk_legacy2rc(rc);
410 
411  free_xml(xml_top);
412  return rc;
413 }
414 
415 int
416 pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force)
417 {
418  pcmk_scheduler_t *scheduler = NULL;
419  pcmk__output_t *out = NULL;
420  int rc = pcmk_rc_ok;
421  cib_t *cib = NULL;
422 
423  rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
424  if (rc != pcmk_rc_ok) {
425  goto done;
426  }
427 
428  rc = pcmk__ticket_remove_attr(out, cib, scheduler, ticket_id, attr_delete, force);
429 
430 done:
431  if (cib != NULL) {
433  }
434 
435  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
437  return rc;
438 }
439 
440 int
442  const char *ticket_id, GHashTable *attr_set, bool force)
443 {
444  xmlNode *ticket_state_xml = NULL;
445  xmlNode *xml_top = NULL;
446  int rc = pcmk_rc_ok;
447 
448  pcmk__assert((out != NULL) && (cib != NULL) && (scheduler != NULL));
449 
450  if (ticket_id == NULL) {
451  return EINVAL;
452  }
453 
454  /* Nothing to do */
455  if (attr_set == NULL || g_hash_table_size(attr_set) == 0) {
456  return pcmk_rc_ok;
457  }
458 
459  rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top);
460 
461  if (rc == pcmk_rc_duplicate_id) {
462  out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id);
463  } else if (rc != pcmk_rc_ok) {
464  free_xml(ticket_state_xml);
465  return rc;
466  }
467 
468  if (!force && g_hash_table_lookup(attr_set, PCMK__XA_GRANTED)) {
469  free_xml(ticket_state_xml);
470  return EACCES;
471  }
472 
473  add_attribute_xml(scheduler, ticket_id, attr_set, &ticket_state_xml);
474 
475  crm_log_xml_debug(xml_top, "Update");
476  rc = cib->cmds->modify(cib, PCMK_XE_STATUS, xml_top, cib_sync_call);
477  rc = pcmk_legacy2rc(rc);
478 
479  free_xml(xml_top);
480  return rc;
481 }
482 
483 int
484 pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set,
485  bool force)
486 {
487  pcmk_scheduler_t *scheduler = NULL;
488  pcmk__output_t *out = NULL;
489  int rc = pcmk_rc_ok;
490  cib_t *cib = NULL;
491 
492  rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
493  if (rc != pcmk_rc_ok) {
494  goto done;
495  }
496 
497  rc = pcmk__ticket_set_attr(out, cib, scheduler, ticket_id, attr_set, force);
498 
499 done:
500  if (cib != NULL) {
502  }
503 
504  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
506  return rc;
507 }
508 
509 int
510 pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
511 {
512  xmlNode *state_xml = NULL;
513  int rc = pcmk_rc_ok;
514 
515  pcmk__assert((out != NULL) && (cib != NULL));
516 
517  rc = pcmk__get_ticket_state(cib, ticket_id, &state_xml);
518 
519  if (rc == pcmk_rc_duplicate_id) {
520  out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s",
521  ticket_id);
522  }
523 
524  if (state_xml != NULL) {
525  out->message(out, "ticket-state", state_xml);
526  free_xml(state_xml);
527  }
528 
529  return rc;
530 }
531 
532 int
533 pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id)
534 {
535  pcmk__output_t *out = NULL;
536  int rc = pcmk_rc_ok;
537  cib_t *cib = NULL;
538 
539  rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml);
540  if (rc != pcmk_rc_ok) {
541  goto done;
542  }
543 
544  rc = pcmk__ticket_state(out, cib, ticket_id);
545 
546 done:
547  if (cib != NULL) {
549  }
550 
551  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
552  return rc;
553 }
#define PCMK__XE_TICKET_STATE
GHashTable * state
Definition: tickets.h:35
#define PCMK_XE_CIB
Definition: xml_names.h:79
#define PCMK_XE_STATUS
Definition: xml_names.h:204
int(* message)(pcmk__output_t *out, const char *message_id,...)
int pcmk__get_ticket_state(cib_t *cib, const char *ticket_id, xmlNode **state)
Definition: pcmk_ticket.c:76
#define PCMK_XE_CONSTRAINTS
Definition: xml_names.h:89
G_GNUC_INTERNAL int pcmk__setup_output_cib_sched(pcmk__output_t **out, cib_t **cib, pcmk_scheduler_t **scheduler, xmlNode **xml)
Definition: pcmk_setup.c:41
int pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set, bool force)
Set the given attribute(s) on a ticket.
Definition: pcmk_ticket.c:484
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition: results.c:704
int pcmk__ticket_info(pcmk__output_t *out, pcmk_scheduler_t *scheduler, const char *ticket_id, bool details, bool raw)
Return information about the given ticket.
Definition: pcmk_ticket.c:312
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:313
High Level API.
int pcmk__ticket_delete(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, const char *ticket_id, bool force)
Definition: pcmk_ticket.c:180
int pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id)
Return a ticket&#39;s state XML.
Definition: pcmk_ticket.c:533
int pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force)
Delete a ticket&#39;s state from the local cluster site.
Definition: pcmk_ticket.c:234
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition: output.c:271
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
GHashTable * tickets
Definition: scheduler.h:222
int pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force)
Remove the given attribute(s) from a ticket.
Definition: pcmk_ticket.c:416
cib_api_operations_t * cmds
Definition: cib_types.h:399
int pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id)
Return information about the given ticket.
Definition: pcmk_ticket.c:343
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id)
Return constraints that apply to the given ticket.
Definition: pcmk_ticket.c:145
int pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id, const char *attr_name, const char *attr_default)
Return the value of a ticket&#39;s attribute.
Definition: pcmk_ticket.c:291
int(* modify)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:228
#define crm_log_xml_debug(xml, text)
Definition: logging.h:411
int pcmk__ticket_set_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, const char *ticket_id, GHashTable *attr_set, bool force)
Set the given attribute(s) on a ticket.
Definition: pcmk_ticket.c:441
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml.c:702
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition: cib_types.h:198
#define PCMK_XA_ID
Definition: xml_names.h:301
int pcmk_legacy2rc(int legacy_rc)
Definition: results.c:561
int pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
Definition: pcmk_ticket.c:510
int(* replace)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:235
void pe__register_messages(pcmk__output_t *out)
Definition: pe_output.c:3451
void free_xml(xmlNode *child)
Definition: xml.c:958
gboolean granted
Definition: tickets.h:32
#define PCMK_XE_TICKETS
Definition: xml_names.h:213
int cib__clean_up_connection(cib_t **cib)
Definition: cib_utils.c:1051
char * id
Definition: tickets.h:31
const char * pcmk_cib_xpath_for(const char *element_name)
Get the relative XPath needed to find a specified CIB element name.
Definition: cib.c:112
#define pcmk__assert(expr)
void pe_free_working_set(pcmk_scheduler_t *scheduler)
Free scheduler data.
Definition: status.c:50
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:695
pcmk__action_result_t result
Definition: pcmk_fence.c:35
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition: xml.c:2306
#define PCMK_XE_RSC_TICKET
Definition: xml_names.h:190
pcmk_scheduler_t * scheduler
#define PCMK_XA_TICKET
Definition: xml_names.h:426
int pcmk__ticket_remove_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler, const char *ticket_id, GList *attr_delete, bool force)
Remove the given attribute(s) from a ticket.
Definition: pcmk_ticket.c:369
#define PCMK__XA_GRANTED
This structure contains everything that makes up a single output formatter.
#define PCMK_XA_LAST_GRANTED
Definition: xml_names.h:315
int pcmk__ticket_get_attr(pcmk__output_t *out, pcmk_scheduler_t *scheduler, const char *ticket_id, const char *attr_name, const char *attr_default)
Definition: pcmk_ticket.c:259
int pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
Definition: pcmk_ticket.c:112
gboolean crm_is_true(const char *s)
Definition: strings.c:500
int(* remove)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:237
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:770