root/lib/pacemaker/pcmk_sched_tickets.c

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

DEFINITIONS

This source file includes following definitions.
  1. ticket_role_matches
  2. constraints_for_ticket
  3. rsc_ticket_new
  4. unpack_rsc_ticket_set
  5. unpack_simple_rsc_ticket
  6. unpack_rsc_ticket_tags
  7. pcmk__unpack_rsc_ticket

   1 /*
   2  * Copyright 2004-2021 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 <stdbool.h>
  13 #include <glib.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/pengine/status.h>
  17 #include <pacemaker-internal.h>
  18 
  19 #include "libpacemaker_private.h"
  20 
  21 /*!
  22  * \brief Check whether a ticket constraint matches a resource by role
  23  *
  24  * \param[in] rsc_ticket  Ticket constraint
  25  * \param[in] rsc         Resource to compare with ticket
  26  *
  27  * \param[in] true if constraint has no role or resource's role matches
  28  *            constraint's, otherwise false
  29  */
  30 static bool
  31 ticket_role_matches(pe_resource_t *rsc_lh, rsc_ticket_t *rsc_ticket)
     /* [previous][next][first][last][top][bottom][index][help] */
  32 {
  33     if ((rsc_ticket->role_lh == RSC_ROLE_UNKNOWN)
  34         || (rsc_ticket->role_lh == rsc_lh->role)) {
  35         return true;
  36     }
  37     pe_rsc_trace(rsc_lh, "LH: Skipping constraint: \"%s\" state filter",
  38                  role2text(rsc_ticket->role_lh));
  39     return false;
  40 }
  41 
  42 /*!
  43  * \brief Create location constraints and fencing as needed for a ticket
  44  *
  45  * \param[in] rsc_lh      Resource affected by ticket
  46  * \param[in] rsc_ticket  Ticket
  47  * \param[in] data_set    Cluster working set
  48  */
  49 static void
  50 constraints_for_ticket(pe_resource_t *rsc_lh, rsc_ticket_t *rsc_ticket,
     /* [previous][next][first][last][top][bottom][index][help] */
  51                        pe_working_set_t *data_set)
  52 {
  53     GList *gIter = NULL;
  54 
  55     CRM_CHECK((rsc_lh != NULL) && (rsc_ticket != NULL), return);
  56 
  57     if (rsc_ticket->ticket->granted && !rsc_ticket->ticket->standby) {
  58         return;
  59     }
  60 
  61     if (rsc_lh->children) {
  62         pe_rsc_trace(rsc_lh, "Processing ticket dependencies from %s", rsc_lh->id);
  63         for (gIter = rsc_lh->children; gIter != NULL; gIter = gIter->next) {
  64             constraints_for_ticket((pe_resource_t *) gIter->data, rsc_ticket,
  65                                   data_set);
  66         }
  67         return;
  68     }
  69 
  70     pe_rsc_trace(rsc_lh, "%s: Processing ticket dependency on %s (%s, %s)",
  71                  rsc_lh->id, rsc_ticket->ticket->id, rsc_ticket->id,
  72                  role2text(rsc_ticket->role_lh));
  73 
  74     if (!rsc_ticket->ticket->granted && (rsc_lh->running_on != NULL)) {
  75 
  76         switch (rsc_ticket->loss_policy) {
  77             case loss_ticket_stop:
  78                 resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__",
  79                                   data_set);
  80                 break;
  81 
  82             case loss_ticket_demote:
  83                 // Promotion score will be set to -INFINITY in promotion_order()
  84                 if (rsc_ticket->role_lh != RSC_ROLE_PROMOTED) {
  85                     resource_location(rsc_lh, NULL, -INFINITY,
  86                                       "__loss_of_ticket__", data_set);
  87                 }
  88                 break;
  89 
  90             case loss_ticket_fence:
  91                 if (!ticket_role_matches(rsc_lh, rsc_ticket)) {
  92                     return;
  93                 }
  94 
  95                 resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__",
  96                                   data_set);
  97 
  98                 for (gIter = rsc_lh->running_on; gIter != NULL;
  99                      gIter = gIter->next) {
 100                     pe_fence_node(data_set, (pe_node_t *) gIter->data,
 101                                   "deadman ticket was lost", FALSE);
 102                 }
 103                 break;
 104 
 105             case loss_ticket_freeze:
 106                 if (!ticket_role_matches(rsc_lh, rsc_ticket)) {
 107                     return;
 108                 }
 109                 if (rsc_lh->running_on != NULL) {
 110                     pe__clear_resource_flags(rsc_lh, pe_rsc_managed);
 111                     pe__set_resource_flags(rsc_lh, pe_rsc_block);
 112                 }
 113                 break;
 114         }
 115 
 116     } else if (!rsc_ticket->ticket->granted) {
 117 
 118         if ((rsc_ticket->role_lh != RSC_ROLE_PROMOTED)
 119             || (rsc_ticket->loss_policy == loss_ticket_stop)) {
 120             resource_location(rsc_lh, NULL, -INFINITY, "__no_ticket__",
 121                               data_set);
 122         }
 123 
 124     } else if (rsc_ticket->ticket->standby) {
 125 
 126         if ((rsc_ticket->role_lh != RSC_ROLE_PROMOTED)
 127             || (rsc_ticket->loss_policy == loss_ticket_stop)) {
 128             resource_location(rsc_lh, NULL, -INFINITY, "__ticket_standby__",
 129                               data_set);
 130         }
 131     }
 132 }
 133 
 134 static void
 135 rsc_ticket_new(const char *id, pe_resource_t *rsc_lh, pe_ticket_t *ticket,
     /* [previous][next][first][last][top][bottom][index][help] */
 136                const char *state_lh, const char *loss_policy,
 137                pe_working_set_t *data_set)
 138 {
 139     rsc_ticket_t *new_rsc_ticket = NULL;
 140 
 141     if (rsc_lh == NULL) {
 142         pcmk__config_err("Ignoring ticket '%s' because resource "
 143                          "does not exist", id);
 144         return;
 145     }
 146 
 147     new_rsc_ticket = calloc(1, sizeof(rsc_ticket_t));
 148     if (new_rsc_ticket == NULL) {
 149         return;
 150     }
 151 
 152     if (pcmk__str_eq(state_lh, RSC_ROLE_STARTED_S,
 153                      pcmk__str_null_matches|pcmk__str_casei)) {
 154         state_lh = RSC_ROLE_UNKNOWN_S;
 155     }
 156 
 157     new_rsc_ticket->id = id;
 158     new_rsc_ticket->ticket = ticket;
 159     new_rsc_ticket->rsc_lh = rsc_lh;
 160     new_rsc_ticket->role_lh = text2role(state_lh);
 161 
 162     if (pcmk__str_eq(loss_policy, "fence", pcmk__str_casei)) {
 163         if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
 164             new_rsc_ticket->loss_policy = loss_ticket_fence;
 165         } else {
 166             pcmk__config_err("Resetting '" XML_TICKET_ATTR_LOSS_POLICY
 167                              "' for ticket '%s' to 'stop' "
 168                              "because fencing is not configured", ticket->id);
 169             loss_policy = "stop";
 170         }
 171     }
 172 
 173     if (new_rsc_ticket->loss_policy == loss_ticket_fence) {
 174         crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)",
 175                   new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
 176                   role2text(new_rsc_ticket->role_lh));
 177 
 178     } else if (pcmk__str_eq(loss_policy, "freeze", pcmk__str_casei)) {
 179         crm_debug("On loss of ticket '%s': Freeze %s (%s)",
 180                   new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
 181                   role2text(new_rsc_ticket->role_lh));
 182         new_rsc_ticket->loss_policy = loss_ticket_freeze;
 183 
 184     } else if (pcmk__str_eq(loss_policy, "demote", pcmk__str_casei)) {
 185         crm_debug("On loss of ticket '%s': Demote %s (%s)",
 186                   new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
 187                   role2text(new_rsc_ticket->role_lh));
 188         new_rsc_ticket->loss_policy = loss_ticket_demote;
 189 
 190     } else if (pcmk__str_eq(loss_policy, "stop", pcmk__str_casei)) {
 191         crm_debug("On loss of ticket '%s': Stop %s (%s)",
 192                   new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
 193                   role2text(new_rsc_ticket->role_lh));
 194         new_rsc_ticket->loss_policy = loss_ticket_stop;
 195 
 196     } else {
 197         if (new_rsc_ticket->role_lh == RSC_ROLE_PROMOTED) {
 198             crm_debug("On loss of ticket '%s': Default to demote %s (%s)",
 199                       new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
 200                       role2text(new_rsc_ticket->role_lh));
 201             new_rsc_ticket->loss_policy = loss_ticket_demote;
 202 
 203         } else {
 204             crm_debug("On loss of ticket '%s': Default to stop %s (%s)",
 205                       new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
 206                       role2text(new_rsc_ticket->role_lh));
 207             new_rsc_ticket->loss_policy = loss_ticket_stop;
 208         }
 209     }
 210 
 211     pe_rsc_trace(rsc_lh, "%s (%s) ==> %s",
 212                  rsc_lh->id, role2text(new_rsc_ticket->role_lh), ticket->id);
 213 
 214     rsc_lh->rsc_tickets = g_list_append(rsc_lh->rsc_tickets, new_rsc_ticket);
 215 
 216     data_set->ticket_constraints = g_list_append(data_set->ticket_constraints,
 217                                                  new_rsc_ticket);
 218 
 219     if (!(new_rsc_ticket->ticket->granted) || new_rsc_ticket->ticket->standby) {
 220         constraints_for_ticket(rsc_lh, new_rsc_ticket, data_set);
 221     }
 222 }
 223 
 224 // \return Standard Pacemaker return code
 225 static int
 226 unpack_rsc_ticket_set(xmlNode *set, pe_ticket_t *ticket,
     /* [previous][next][first][last][top][bottom][index][help] */
 227                       const char *loss_policy, pe_working_set_t *data_set)
 228 {
 229     const char *set_id = NULL;
 230     const char *role = NULL;
 231 
 232     CRM_CHECK(set != NULL, return EINVAL);
 233     CRM_CHECK(ticket != NULL, return EINVAL);
 234 
 235     set_id = ID(set);
 236     if (set_id == NULL) {
 237         pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without "
 238                          XML_ATTR_ID);
 239         return pcmk_rc_schema_validation;
 240     }
 241 
 242     role = crm_element_value(set, "role");
 243 
 244     for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 245          xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 246 
 247         pe_resource_t *resource = NULL;
 248 
 249         resource = pcmk__find_constraint_resource(data_set->resources,
 250                                                   ID(xml_rsc));
 251         if (resource == NULL) {
 252             pcmk__config_err("%s: No resource found for %s",
 253                              set_id, ID(xml_rsc));
 254             return pcmk_rc_schema_validation;
 255         }
 256         pe_rsc_trace(resource, "Resource '%s' depends on ticket '%s'",
 257                      resource->id, ticket->id);
 258         rsc_ticket_new(set_id, resource, ticket, role, loss_policy, data_set);
 259     }
 260 
 261     return pcmk_rc_ok;
 262 }
 263 
 264 static void
 265 unpack_simple_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 266 {
 267     const char *id = NULL;
 268     const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
 269     const char *loss_policy = crm_element_value(xml_obj,
 270                                                 XML_TICKET_ATTR_LOSS_POLICY);
 271 
 272     pe_ticket_t *ticket = NULL;
 273 
 274     const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
 275     const char *state_lh = crm_element_value(xml_obj,
 276                                              XML_COLOC_ATTR_SOURCE_ROLE);
 277 
 278     // experimental syntax from pacemaker-next (unlikely to be adopted as-is)
 279     const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE);
 280 
 281     pe_resource_t *rsc_lh = NULL;
 282 
 283     CRM_CHECK(xml_obj != NULL, return);
 284 
 285     id = ID(xml_obj);
 286     if (id == NULL) {
 287         pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
 288                          crm_element_name(xml_obj));
 289         return;
 290     }
 291 
 292     if (ticket_str == NULL) {
 293         pcmk__config_err("Ignoring constraint '%s' without ticket specified",
 294                          id);
 295         return;
 296     } else {
 297         ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
 298     }
 299 
 300     if (ticket == NULL) {
 301         pcmk__config_err("Ignoring constraint '%s' because ticket '%s' "
 302                          "does not exist", id, ticket_str);
 303         return;
 304     }
 305 
 306     if (id_lh == NULL) {
 307         pcmk__config_err("Ignoring constraint '%s' without resource", id);
 308         return;
 309     } else {
 310         rsc_lh = pcmk__find_constraint_resource(data_set->resources, id_lh);
 311     }
 312 
 313     if (rsc_lh == NULL) {
 314         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 315                          "does not exist", id, id_lh);
 316         return;
 317 
 318     } else if ((instance_lh != NULL) && !pe_rsc_is_clone(rsc_lh)) {
 319         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 320                          "is not a clone but instance '%s' was requested",
 321                          id, id_lh, instance_lh);
 322         return;
 323     }
 324 
 325     if (instance_lh != NULL) {
 326         rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set);
 327         if (rsc_lh == NULL) {
 328             pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
 329                               "does not have an instance '%s'",
 330                               "'%s'", id, id_lh, instance_lh);
 331             return;
 332         }
 333     }
 334 
 335     rsc_ticket_new(id, rsc_lh, ticket, state_lh, loss_policy, data_set);
 336 }
 337 
 338 // \return Standard Pacemaker return code
 339 static int
 340 unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 341                        pe_working_set_t *data_set)
 342 {
 343     const char *id = NULL;
 344     const char *id_lh = NULL;
 345     const char *state_lh = NULL;
 346 
 347     pe_resource_t *rsc_lh = NULL;
 348     pe_tag_t *tag_lh = NULL;
 349 
 350     xmlNode *rsc_set_lh = NULL;
 351 
 352     *expanded_xml = NULL;
 353 
 354     CRM_CHECK(xml_obj != NULL, return EINVAL);
 355 
 356     id = ID(xml_obj);
 357     if (id == NULL) {
 358         pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
 359                          crm_element_name(xml_obj));
 360         return pcmk_rc_schema_validation;
 361     }
 362 
 363     // Check whether there are any resource sets with template or tag references
 364     *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
 365     if (*expanded_xml != NULL) {
 366         crm_log_xml_trace(*expanded_xml, "Expanded rsc_ticket");
 367         return pcmk_rc_ok;
 368     }
 369 
 370     id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
 371     if (id_lh == NULL) {
 372         return pcmk_rc_ok;
 373     }
 374 
 375     if (!pcmk__valid_resource_or_tag(data_set, id_lh, &rsc_lh, &tag_lh)) {
 376         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 377                          "valid resource or tag", id, id_lh);
 378         return pcmk_rc_schema_validation;
 379 
 380     } else if (rsc_lh) {
 381         // No template or tag is referenced
 382         return pcmk_rc_ok;
 383     }
 384 
 385     state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
 386 
 387     *expanded_xml = copy_xml(xml_obj);
 388 
 389     // Convert template/tag reference in "rsc" into resource_set under rsc_ticket
 390     if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_lh, XML_COLOC_ATTR_SOURCE,
 391                           false, data_set)) {
 392         free_xml(*expanded_xml);
 393         *expanded_xml = NULL;
 394         return pcmk_rc_schema_validation;
 395     }
 396 
 397     if (rsc_set_lh != NULL) {
 398         if (state_lh != NULL) {
 399             // Move "rsc-role" into converted resource_set as a "role" attribute
 400             crm_xml_add(rsc_set_lh, "role", state_lh);
 401             xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE);
 402         }
 403 
 404     } else {
 405         free_xml(*expanded_xml);
 406         *expanded_xml = NULL;
 407     }
 408 
 409     return pcmk_rc_ok;
 410 }
 411 
 412 void
 413 pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 414 {
 415     xmlNode *set = NULL;
 416     bool any_sets = false;
 417 
 418     const char *id = NULL;
 419     const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
 420     const char *loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
 421 
 422     pe_ticket_t *ticket = NULL;
 423 
 424     xmlNode *orig_xml = NULL;
 425     xmlNode *expanded_xml = NULL;
 426 
 427     CRM_CHECK(xml_obj != NULL, return);
 428 
 429     id = ID(xml_obj);
 430     if (id == NULL) {
 431         pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
 432                          crm_element_name(xml_obj));
 433         return;
 434     }
 435 
 436     if (data_set->tickets == NULL) {
 437         data_set->tickets = pcmk__strkey_table(free, destroy_ticket);
 438     }
 439 
 440     if (ticket_str == NULL) {
 441         pcmk__config_err("Ignoring constraint '%s' without ticket", id);
 442         return;
 443     } else {
 444         ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
 445     }
 446 
 447     if (ticket == NULL) {
 448         ticket = ticket_new(ticket_str, data_set);
 449         if (ticket == NULL) {
 450             return;
 451         }
 452     }
 453 
 454     if (unpack_rsc_ticket_tags(xml_obj, &expanded_xml,
 455                                data_set) != pcmk_rc_ok) {
 456         return;
 457     }
 458     if (expanded_xml != NULL) {
 459         orig_xml = xml_obj;
 460         xml_obj = expanded_xml;
 461     }
 462 
 463     for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
 464          set = crm_next_same_xml(set)) {
 465 
 466         any_sets = true;
 467         set = expand_idref(set, data_set->input);
 468         if ((set == NULL) // Configuration error, message already logged
 469             || (unpack_rsc_ticket_set(set, ticket, loss_policy,
 470                                       data_set) != pcmk_rc_ok)) {
 471             if (expanded_xml != NULL) {
 472                 free_xml(expanded_xml);
 473             }
 474             return;
 475         }
 476     }
 477 
 478     if (expanded_xml) {
 479         free_xml(expanded_xml);
 480         xml_obj = orig_xml;
 481     }
 482 
 483     if (!any_sets) {
 484         unpack_simple_rsc_ticket(xml_obj, data_set);
 485     }
 486 }

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