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         pcmk__xml_free(*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     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)
  66                 || !pcmk_is_set(ticket->flags, pcmk__ticket_granted))
  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 
  77 int
  78 pcmk__get_ticket_state(cib_t *cib, const char *ticket_id, xmlNode **state)
     /* [previous][next][first][last][top][bottom][index][help] */
  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     *state = NULL;
  86 
  87     if (ticket_id != NULL) {
  88         xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS
  89                                   "/" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"%s\"]",
  90                                   ticket_id);
  91     } else {
  92         xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS);
  93     }
  94 
  95     rc = cib->cmds->query(cib, xpath, &xml_search, cib_sync_call|cib_xpath);
  96     rc = pcmk_legacy2rc(rc);
  97 
  98     if (rc == pcmk_rc_ok) {
  99         crm_log_xml_debug(xml_search, "Match");
 100 
 101         if (xml_search->children != NULL && ticket_id != NULL) {
 102             rc = pcmk_rc_duplicate_id;
 103         }
 104     }
 105 
 106     free(xpath);
 107 
 108     *state = xml_search;
 109     return rc;
 110 }
 111 
 112 int
 113 pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 114 {
 115     int rc = pcmk_rc_ok;
 116     xmlNode *result = NULL;
 117     const char *xpath_base = NULL;
 118     char *xpath = NULL;
 119 
 120     pcmk__assert((out != NULL) && (cib != NULL));
 121 
 122     xpath_base = pcmk_cib_xpath_for(PCMK_XE_CONSTRAINTS);
 123     pcmk__assert(xpath_base != NULL);
 124 
 125     if (ticket_id != NULL) {
 126         xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET "[@" PCMK_XA_TICKET "=\"%s\"]",
 127                                   xpath_base, ticket_id);
 128     } else {
 129         xpath = crm_strdup_printf("%s/" PCMK_XE_RSC_TICKET, xpath_base);
 130     }
 131 
 132     rc = cib->cmds->query(cib, xpath, &result, cib_sync_call|cib_xpath);
 133     rc = pcmk_legacy2rc(rc);
 134 
 135     if (result != NULL) {
 136         out->message(out, "ticket-constraints", result);
 137         pcmk__xml_free(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 = NULL;
 194 
 195         ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
 196                                      ticket_id);
 197         if (ticket == NULL) {
 198             return ENXIO;
 199         }
 200 
 201         if (pcmk_is_set(ticket->flags, pcmk__ticket_granted)) {
 202             return EACCES;
 203         }
 204     }
 205 
 206     rc = pcmk__get_ticket_state(cib, ticket_id, &state);
 207 
 208     if (rc == pcmk_rc_duplicate_id) {
 209         out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s",
 210                   ticket_id);
 211 
 212     } else if (rc == ENXIO) {
 213         return pcmk_rc_ok;
 214 
 215     } else if (rc != pcmk_rc_ok) {
 216         return rc;
 217     }
 218 
 219     crm_log_xml_debug(state, "Delete");
 220 
 221     if (rc == pcmk_rc_duplicate_id) {
 222         rc = pcmk__xe_foreach_child(state, NULL, delete_single_ticket, cib);
 223     } else {
 224         rc = delete_single_ticket(state, cib);
 225     }
 226 
 227     if (rc == pcmk_rc_ok) {
 228         out->info(out, "Cleaned up %s", ticket_id);
 229     }
 230 
 231     pcmk__xml_free(state);
 232     return rc;
 233 }
 234 
 235 int
 236 pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force)
     /* [previous][next][first][last][top][bottom][index][help] */
 237 {
 238     pcmk_scheduler_t *scheduler = NULL;
 239     pcmk__output_t *out = NULL;
 240     cib_t *cib = NULL;
 241     int rc = pcmk_rc_ok;
 242 
 243     rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
 244     if (rc != pcmk_rc_ok) {
 245         goto done;
 246     }
 247 
 248     rc = pcmk__ticket_delete(out, cib, scheduler, ticket_id, force);
 249 
 250 done:
 251     if (cib != NULL) {
 252         cib__clean_up_connection(&cib);
 253     }
 254 
 255     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 256     pe_free_working_set(scheduler);
 257     return rc;
 258 }
 259 
 260 int
 261 pcmk__ticket_get_attr(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 262                       const char *ticket_id, const char *attr_name,
 263                       const char *attr_default)
 264 {
 265     int rc = pcmk_rc_ok;
 266     const char *attr_value = NULL;
 267     pcmk__ticket_t *ticket = NULL;
 268 
 269     pcmk__assert((out != NULL) && (scheduler != NULL));
 270 
 271     if (ticket_id == NULL || attr_name == NULL) {
 272         return EINVAL;
 273     }
 274 
 275     ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
 276                                  ticket_id);
 277 
 278     if (ticket != NULL) {
 279         attr_value = g_hash_table_lookup(ticket->state, attr_name);
 280     }
 281 
 282     if (attr_value != NULL) {
 283         out->message(out, "ticket-attribute", ticket_id, attr_name, attr_value);
 284     } else if (attr_default != NULL) {
 285         out->message(out, "ticket-attribute", ticket_id, attr_name, attr_default);
 286     } else {
 287         rc = ENXIO;
 288     }
 289 
 290     return rc;
 291 }
 292 
 293 int
 294 pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 295                      const char *attr_name, const char *attr_default)
 296 {
 297     pcmk_scheduler_t *scheduler = NULL;
 298     pcmk__output_t *out = NULL;
 299     int rc = pcmk_rc_ok;
 300 
 301     rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml);
 302     if (rc != pcmk_rc_ok) {
 303         goto done;
 304     }
 305 
 306     rc = pcmk__ticket_get_attr(out, scheduler, ticket_id, attr_name, attr_default);
 307 
 308 done:
 309     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 310     pe_free_working_set(scheduler);
 311     return rc;
 312 }
 313 
 314 int
 315 pcmk__ticket_info(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 316                   const char *ticket_id, bool details, bool raw)
 317 {
 318     int rc = pcmk_rc_ok;
 319 
 320     pcmk__assert((out != NULL) && (scheduler != NULL));
 321 
 322     if (ticket_id != NULL) {
 323         GHashTable *tickets = NULL;
 324         pcmk__ticket_t *ticket = NULL;
 325 
 326         ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
 327                                      ticket_id);
 328         if (ticket == NULL) {
 329             return ENXIO;
 330         }
 331 
 332         /* The ticket-list message expects a GHashTable, so we'll construct
 333          * one with just this single item.
 334          */
 335         tickets = pcmk__strkey_table(free, NULL);
 336         g_hash_table_insert(tickets, strdup(ticket->id), ticket);
 337         out->message(out, "ticket-list", tickets, false, raw, details);
 338         g_hash_table_destroy(tickets);
 339 
 340     } else {
 341         out->message(out, "ticket-list", scheduler->priv->ticket_constraints,
 342                      false, raw, details);
 343     }
 344 
 345     return rc;
 346 }
 347 
 348 int
 349 pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 350 {
 351     pcmk_scheduler_t *scheduler = NULL;
 352     pcmk__output_t *out = NULL;
 353     int rc = pcmk_rc_ok;
 354 
 355     rc = pcmk__setup_output_cib_sched(&out, NULL, &scheduler, xml);
 356     if (rc != pcmk_rc_ok) {
 357         goto done;
 358     }
 359 
 360     pe__register_messages(out);
 361 
 362     /* XML output (which is the only format supported by public API functions
 363      * due to the use of pcmk__xml_output_new above) always prints all details,
 364      * so just pass false for the last two arguments.
 365      */
 366     rc = pcmk__ticket_info(out, scheduler, ticket_id, false, false);
 367 
 368 done:
 369     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 370     pe_free_working_set(scheduler);
 371     return rc;
 372 }
 373 
 374 int
 375 pcmk__ticket_remove_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 376                          const char *ticket_id, GList *attr_delete, bool force)
 377 {
 378     xmlNode *ticket_state_xml = NULL;
 379     xmlNode *xml_top = NULL;
 380     int rc = pcmk_rc_ok;
 381 
 382     pcmk__assert((out != NULL) && (cib != NULL) && (scheduler != NULL));
 383 
 384     if (ticket_id == NULL) {
 385         return EINVAL;
 386     }
 387 
 388     /* Nothing to do */
 389     if (attr_delete == NULL) {
 390         return pcmk_rc_ok;
 391     }
 392 
 393     rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top);
 394 
 395     if (rc == pcmk_rc_duplicate_id) {
 396         out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id);
 397     } else if (rc != pcmk_rc_ok) {
 398         pcmk__xml_free(ticket_state_xml);
 399         return rc;
 400     }
 401 
 402     for (GList *list_iter = attr_delete; list_iter != NULL; list_iter = list_iter->next) {
 403         const char *key = list_iter->data;
 404 
 405         if (!force && pcmk__str_eq(key, PCMK__XA_GRANTED, pcmk__str_none)) {
 406             pcmk__xml_free(ticket_state_xml);
 407             return EACCES;
 408         }
 409 
 410         pcmk__xe_remove_attr(ticket_state_xml, key);
 411     }
 412 
 413     crm_log_xml_debug(xml_top, "Replace");
 414     rc = cib->cmds->replace(cib, PCMK_XE_STATUS, ticket_state_xml, cib_sync_call);
 415     rc = pcmk_legacy2rc(rc);
 416 
 417     pcmk__xml_free(xml_top);
 418     return rc;
 419 }
 420 
 421 int
 422 pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force)
     /* [previous][next][first][last][top][bottom][index][help] */
 423 {
 424     pcmk_scheduler_t *scheduler = NULL;
 425     pcmk__output_t *out = NULL;
 426     int rc = pcmk_rc_ok;
 427     cib_t *cib = NULL;
 428 
 429     rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
 430     if (rc != pcmk_rc_ok) {
 431         goto done;
 432     }
 433 
 434     rc = pcmk__ticket_remove_attr(out, cib, scheduler, ticket_id, attr_delete, force);
 435 
 436 done:
 437     if (cib != NULL) {
 438         cib__clean_up_connection(&cib);
 439     }
 440 
 441     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 442     pe_free_working_set(scheduler);
 443     return rc;
 444 }
 445 
 446 int
 447 pcmk__ticket_set_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 448                       const char *ticket_id, GHashTable *attr_set, bool force)
 449 {
 450     xmlNode *ticket_state_xml = NULL;
 451     xmlNode *xml_top = NULL;
 452     int rc = pcmk_rc_ok;
 453 
 454     pcmk__assert((out != NULL) && (cib != NULL) && (scheduler != NULL));
 455 
 456     if (ticket_id == NULL) {
 457         return EINVAL;
 458     }
 459 
 460     /* Nothing to do */
 461     if (attr_set == NULL || g_hash_table_size(attr_set) == 0) {
 462         return pcmk_rc_ok;
 463     }
 464 
 465     rc = build_ticket_modify_xml(cib, ticket_id, &ticket_state_xml, &xml_top);
 466 
 467     if (rc == pcmk_rc_duplicate_id) {
 468         out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s", ticket_id);
 469     } else if (rc != pcmk_rc_ok) {
 470         pcmk__xml_free(ticket_state_xml);
 471         return rc;
 472     }
 473 
 474     if (!force && g_hash_table_lookup(attr_set, PCMK__XA_GRANTED)) {
 475         pcmk__xml_free(ticket_state_xml);
 476         return EACCES;
 477     }
 478 
 479     add_attribute_xml(scheduler, ticket_id, attr_set, &ticket_state_xml);
 480 
 481     crm_log_xml_debug(xml_top, "Update");
 482     rc = cib->cmds->modify(cib, PCMK_XE_STATUS, xml_top, cib_sync_call);
 483     rc = pcmk_legacy2rc(rc);
 484 
 485     pcmk__xml_free(xml_top);
 486     return rc;
 487 }
 488 
 489 int
 490 pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 491                      bool force)
 492 {
 493     pcmk_scheduler_t *scheduler = NULL;
 494     pcmk__output_t *out = NULL;
 495     int rc = pcmk_rc_ok;
 496     cib_t *cib = NULL;
 497 
 498     rc = pcmk__setup_output_cib_sched(&out, &cib, &scheduler, xml);
 499     if (rc != pcmk_rc_ok) {
 500         goto done;
 501     }
 502 
 503     rc = pcmk__ticket_set_attr(out, cib, scheduler, ticket_id, attr_set, force);
 504 
 505 done:
 506     if (cib != NULL) {
 507         cib__clean_up_connection(&cib);
 508     }
 509 
 510     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 511     pe_free_working_set(scheduler);
 512     return rc;
 513 }
 514 
 515 int
 516 pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 517 {
 518     xmlNode *state_xml = NULL;
 519     int rc = pcmk_rc_ok;
 520 
 521     pcmk__assert((out != NULL) && (cib != NULL));
 522 
 523     rc = pcmk__get_ticket_state(cib, ticket_id, &state_xml);
 524 
 525     if (rc == pcmk_rc_duplicate_id) {
 526         out->info(out, "Multiple " PCMK__XE_TICKET_STATE "s match ticket=%s",
 527                   ticket_id);
 528     }
 529 
 530     if (state_xml != NULL) {
 531         out->message(out, "ticket-state", state_xml);
 532         pcmk__xml_free(state_xml);
 533     }
 534 
 535     return rc;
 536 }
 537 
 538 int
 539 pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 540 {
 541     pcmk__output_t *out = NULL;
 542     int rc = pcmk_rc_ok;
 543     cib_t *cib = NULL;
 544 
 545     rc = pcmk__setup_output_cib_sched(&out, &cib, NULL, xml);
 546     if (rc != pcmk_rc_ok) {
 547         goto done;
 548     }
 549 
 550     rc = pcmk__ticket_state(out, cib, ticket_id);
 551 
 552 done:
 553     if (cib != NULL) {
 554         cib__clean_up_connection(&cib);
 555     }
 556 
 557     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 558     return rc;
 559 }

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