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