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     // cppcheck doesn't understand the above pcmk__assert line
  86     // cppcheck-suppress ctunullpointer
  87     *state = NULL;
  88 
  89     if (ticket_id != NULL) {
  90         xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS
  91                                   "/" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"%s\"]",
  92                                   ticket_id);
  93     } else {
  94         xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS);
  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) {
 104             rc = pcmk_rc_duplicate_id;
 105         }
 106     }
 107 
 108     free(xpath);
 109 
 110     *state = xml_search;
 111     return rc;
 112 }
 113 
 114 int
 115 pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 124     xpath_base = pcmk_cib_xpath_for(PCMK_XE_CONSTRAINTS);
 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);
 139         pcmk__xml_free(result);
 140     }
 141 
 142     free(xpath);
 143     return rc;
 144 }
 145 
 146 int
 147 pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 160 done:
 161     if (cib != NULL) {
 162         cib__clean_up_connection(&cib);
 163     }
 164 
 165     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 166     return rc;
 167 }
 168 
 169 static int
 170 delete_single_ticket(xmlNode *child, void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 181 int
 182 pcmk__ticket_delete(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 237 int
 238 pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force)
     /* [previous][next][first][last][top][bottom][index][help] */
 239 {
 240     pcmk_scheduler_t *scheduler = NULL;
 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 
 252 done:
 253     if (cib != NULL) {
 254         cib__clean_up_connection(&cib);
 255     }
 256 
 257     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 258     pcmk_free_scheduler(scheduler);
 259     return rc;
 260 }
 261 
 262 int
 263 pcmk__ticket_get_attr(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 295 int
 296 pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 297                      const char *attr_name, const char *attr_default)
 298 {
 299     pcmk_scheduler_t *scheduler = NULL;
 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 
 310 done:
 311     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 312     pcmk_free_scheduler(scheduler);
 313     return rc;
 314 }
 315 
 316 int
 317 pcmk__ticket_info(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 350 int
 351 pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 352 {
 353     pcmk_scheduler_t *scheduler = NULL;
 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 
 362     pe__register_messages(out);
 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 
 370 done:
 371     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 372     pcmk_free_scheduler(scheduler);
 373     return rc;
 374 }
 375 
 376 int
 377 pcmk__ticket_remove_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 423 int
 424 pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete, bool force)
     /* [previous][next][first][last][top][bottom][index][help] */
 425 {
 426     pcmk_scheduler_t *scheduler = NULL;
 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 
 438 done:
 439     if (cib != NULL) {
 440         cib__clean_up_connection(&cib);
 441     }
 442 
 443     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 444     pcmk_free_scheduler(scheduler);
 445     return rc;
 446 }
 447 
 448 int
 449 pcmk__ticket_set_attr(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 491 int
 492 pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 493                      bool force)
 494 {
 495     pcmk_scheduler_t *scheduler = NULL;
 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 
 507 done:
 508     if (cib != NULL) {
 509         cib__clean_up_connection(&cib);
 510     }
 511 
 512     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 513     pcmk_free_scheduler(scheduler);
 514     return rc;
 515 }
 516 
 517 int
 518 pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 540 int
 541 pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 554 done:
 555     if (cib != NULL) {
 556         cib__clean_up_connection(&cib);
 557     }
 558 
 559     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 560     return rc;
 561 }

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