pacemaker 2.1.8-2.1.8
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 free_xml(*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
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
75int
76pcmk__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 CRM_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
111int
112pcmk__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 CRM_ASSERT(out != NULL && cib != NULL);
120
122 CRM_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);
138 }
139
140 free(xpath);
141 return rc;
142}
143
144int
145pcmk_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
158done:
159 if (cib != NULL) {
161 }
162
164 return rc;
165}
166
167static int
168delete_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
179int
181 const char *ticket_id, bool force)
182{
183 int rc = pcmk_rc_ok;
184 xmlNode *state = NULL;
185
186 CRM_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
233int
234pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force)
235{
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
248done:
249 if (cib != NULL) {
251 }
252
255 return rc;
256}
257
258int
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 CRM_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
290int
291pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id,
292 const char *attr_name, const char *attr_default)
293{
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
305done:
308 return rc;
309}
310
311int
313 const char *ticket_id, bool details, bool raw)
314{
315 int rc = pcmk_rc_ok;
316
317 CRM_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
342int
343pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id)
344{
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
362done:
365 return rc;
366}
367
368int
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 CRM_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
415int
416pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force)
417{
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
430done:
431 if (cib != NULL) {
433 }
434
437 return rc;
438}
439
440int
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 CRM_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
483int
484pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set,
485 bool force)
486{
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
499done:
500 if (cib != NULL) {
502 }
503
506 return rc;
507}
508
509int
510pcmk__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 CRM_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
532int
533pcmk_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
546done:
547 if (cib != NULL) {
549 }
550
552 return rc;
553}
int cib__clean_up_connection(cib_t **cib)
Definition cib_utils.c:1046
@ cib_scope_local
Definition cib_types.h:90
@ cib_xpath
Definition cib_types.h:63
@ cib_sync_call
Definition cib_types.h:133
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
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
gboolean crm_is_true(const char *s)
Definition strings.c:488
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
#define crm_log_xml_debug(xml, text)
Definition logging.h:409
pcmk_scheduler_t * scheduler
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:301
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition output.c:271
High Level API.
pcmk__action_result_t result
Definition pcmk_fence.c:35
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:76
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:3440
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_duplicate_id
Definition results.h:124
int pcmk_legacy2rc(int legacy_rc)
Definition results.c:559
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition results.c:702
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:683
@ pcmk__str_none
int(* remove)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition cib_types.h:237
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition cib_types.h:198
int(* replace)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition cib_types.h:235
int(* modify)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition cib_types.h:228
cib_api_operations_t * cmds
Definition cib_types.h:399
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
GHashTable * state
Definition tickets.h:35
char * id
Definition tickets.h:31
gboolean granted
Definition tickets.h:32
GHashTable * tickets
Definition scheduler.h:222
void free_xml(xmlNode *child)
Definition xml.c:867
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition xml.c:652
int pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, int(*handler)(xmlNode *xml, void *userdata), void *userdata)
Definition xml.c:2288
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition xml.c:720
#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:199
#define PCMK_XE_RSC_TICKET
Definition xml_names.h:186
#define PCMK_XA_TICKET
Definition xml_names.h:421
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XE_TICKETS
Definition xml_names.h:208
#define PCMK_XA_LAST_GRANTED
Definition xml_names.h:310
#define PCMK__XA_GRANTED
#define PCMK__XE_TICKET_STATE