root/lib/pacemaker/pcmk_ticket.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. build_ticket_modify_xml
  2. add_attribute_xml
  3. pcmk__get_ticket_state
  4. pcmk__ticket_constraints
  5. pcmk_ticket_constraints
  6. delete_single_ticket
  7. pcmk__ticket_delete
  8. pcmk_ticket_delete
  9. pcmk__ticket_get_attr
  10. pcmk_ticket_get_attr
  11. pcmk__ticket_info
  12. pcmk_ticket_info
  13. pcmk__ticket_remove_attr
  14. pcmk_ticket_remove_attr
  15. pcmk__ticket_set_attr
  16. pcmk_ticket_set_attr
  17. pcmk__ticket_state
  18. pcmk_ticket_state

   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,
     /* [previous][next][first][last][top][bottom][index][help] */
  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,
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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) {
  86         xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS
  87                                   "/" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"%s\"]",
  88                                   ticket_id);
  89     } else {
  90         xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS);
  91     }
  92 
  93     rc = cib->cmds->query(cib, xpath, &xml_search,
  94                           cib_sync_call | cib_scope_local | cib_xpath);
  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) {
 101             rc = pcmk_rc_duplicate_id;
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 121     xpath_base = pcmk_cib_xpath_for(PCMK_XE_CONSTRAINTS);
 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,
 132                           cib_sync_call | cib_scope_local | cib_xpath);
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
 160         cib__clean_up_connection(&cib);
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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
 180 pcmk__ticket_delete(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
 250         cib__clean_up_connection(&cib);
 251     }
 252 
 253     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 254     pe_free_working_set(scheduler);
 255     return rc;
 256 }
 257 
 258 int
 259 pcmk__ticket_get_attr(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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);
 307     pe_free_working_set(scheduler);
 308     return rc;
 309 }
 310 
 311 int
 312 pcmk__ticket_info(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 354     pe__register_messages(out);
 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);
 364     pe_free_working_set(scheduler);
 365     return rc;
 366 }
 367 
 368 int
 369 pcmk__ticket_remove_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
 432         cib__clean_up_connection(&cib);
 433     }
 434 
 435     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 436     pe_free_working_set(scheduler);
 437     return rc;
 438 }
 439 
 440 int
 441 pcmk__ticket_set_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
 501         cib__clean_up_connection(&cib);
 502     }
 503 
 504     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 505     pe_free_working_set(scheduler);
 506     return rc;
 507 }
 508 
 509 int
 510 pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
 548         cib__clean_up_connection(&cib);
 549     }
 550 
 551     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 552     return rc;
 553 }

/* [previous][next][first][last][top][bottom][index][help] */