root/lib/pacemaker/pcmk_sched_utils.c

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

DEFINITIONS

This source file includes following definitions.
  1. rsc2node_new
  2. can_run_resources
  3. pcmk__copy_node_table
  4. pcmk__copy_node_list
  5. sort_node_weight
  6. sort_nodes_by_weight
  7. native_deallocate
  8. native_assign_node
  9. log_action
  10. can_run_any
  11. create_pseudo_resource_op
  12. pe_cancel_op
  13. sched_shutdown_op
  14. generate_transition_magic
  15. append_digest
  16. pcmk__create_history_xml
  17. pcmk__new_logger

   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 #include <crm/msg_xml.h>
  12 #include <crm/lrmd.h>       // lrmd_event_data_t
  13 #include <crm/common/xml_internal.h>
  14 #include <pacemaker-internal.h>
  15 
  16 pe__location_t *
  17 rsc2node_new(const char *id, pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
  18              int node_weight, const char *discover_mode,
  19              pe_node_t *foo_node, pe_working_set_t *data_set)
  20 {
  21     pe__location_t *new_con = NULL;
  22 
  23     if (rsc == NULL || id == NULL) {
  24         pe_err("Invalid constraint %s for rsc=%p", crm_str(id), rsc);
  25         return NULL;
  26 
  27     } else if (foo_node == NULL) {
  28         CRM_CHECK(node_weight == 0, return NULL);
  29     }
  30 
  31     new_con = calloc(1, sizeof(pe__location_t));
  32     if (new_con != NULL) {
  33         new_con->id = strdup(id);
  34         new_con->rsc_lh = rsc;
  35         new_con->node_list_rh = NULL;
  36         new_con->role_filter = RSC_ROLE_UNKNOWN;
  37 
  38 
  39         if (pcmk__str_eq(discover_mode, "always", pcmk__str_null_matches | pcmk__str_casei)) {
  40             new_con->discover_mode = pe_discover_always;
  41         } else if (pcmk__str_eq(discover_mode, "never", pcmk__str_casei)) {
  42             new_con->discover_mode = pe_discover_never;
  43         } else if (pcmk__str_eq(discover_mode, "exclusive", pcmk__str_casei)) {
  44             new_con->discover_mode = pe_discover_exclusive;
  45             rsc->exclusive_discover = TRUE;
  46         } else {
  47             pe_err("Invalid %s value %s in location constraint", XML_LOCATION_ATTR_DISCOVERY, discover_mode);
  48         }
  49 
  50         if (foo_node != NULL) {
  51             pe_node_t *copy = pe__copy_node(foo_node);
  52 
  53             copy->weight = node_weight;
  54             new_con->node_list_rh = g_list_prepend(NULL, copy);
  55         }
  56 
  57         data_set->placement_constraints = g_list_prepend(data_set->placement_constraints, new_con);
  58         rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
  59     }
  60 
  61     return new_con;
  62 }
  63 
  64 gboolean
  65 can_run_resources(const pe_node_t * node)
     /* [previous][next][first][last][top][bottom][index][help] */
  66 {
  67     if (node == NULL) {
  68         return FALSE;
  69     }
  70 #if 0
  71     if (node->weight < 0) {
  72         return FALSE;
  73     }
  74 #endif
  75 
  76     if (node->details->online == FALSE
  77         || node->details->shutdown || node->details->unclean
  78         || node->details->standby || node->details->maintenance) {
  79         crm_trace("%s: online=%d, unclean=%d, standby=%d, maintenance=%d",
  80                   node->details->uname, node->details->online,
  81                   node->details->unclean, node->details->standby, node->details->maintenance);
  82         return FALSE;
  83     }
  84     return TRUE;
  85 }
  86 
  87 /*!
  88  * \internal
  89  * \brief Copy a hash table of node objects
  90  *
  91  * \param[in] nodes  Hash table to copy
  92  *
  93  * \return New copy of nodes (or NULL if nodes is NULL)
  94  */
  95 GHashTable *
  96 pcmk__copy_node_table(GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
  97 {
  98     GHashTable *new_table = NULL;
  99     GHashTableIter iter;
 100     pe_node_t *node = NULL;
 101 
 102     if (nodes == NULL) {
 103         return NULL;
 104     }
 105     new_table = pcmk__strkey_table(NULL, free);
 106     g_hash_table_iter_init(&iter, nodes);
 107     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
 108         pe_node_t *new_node = pe__copy_node(node);
 109 
 110         g_hash_table_insert(new_table, (gpointer) new_node->details->id,
 111                             new_node);
 112     }
 113     return new_table;
 114 }
 115 
 116 /*!
 117  * \internal
 118  * \brief Copy a list of node objects
 119  *
 120  * \param[in] list   List to copy
 121  * \param[in] reset  Set copies' scores to 0
 122  *
 123  * \return New list of shallow copies of nodes in original list
 124  */
 125 GList *
 126 pcmk__copy_node_list(const GList *list, bool reset)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128     GList *result = NULL;
 129 
 130     for (const GList *gIter = list; gIter != NULL; gIter = gIter->next) {
 131         pe_node_t *new_node = NULL;
 132         pe_node_t *this_node = (pe_node_t *) gIter->data;
 133 
 134         new_node = pe__copy_node(this_node);
 135         if (reset) {
 136             new_node->weight = 0;
 137         }
 138         result = g_list_prepend(result, new_node);
 139     }
 140     return result;
 141 }
 142 
 143 struct node_weight_s {
 144     pe_node_t *active;
 145     pe_working_set_t *data_set;
 146 };
 147 
 148 /* return -1 if 'a' is more preferred
 149  * return  1 if 'b' is more preferred
 150  */
 151 
 152 static gint
 153 sort_node_weight(gconstpointer a, gconstpointer b, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 154 {
 155     const pe_node_t *node1 = (const pe_node_t *)a;
 156     const pe_node_t *node2 = (const pe_node_t *)b;
 157     struct node_weight_s *nw = data;
 158 
 159     int node1_weight = 0;
 160     int node2_weight = 0;
 161 
 162     int result = 0;
 163 
 164     if (a == NULL) {
 165         return 1;
 166     }
 167     if (b == NULL) {
 168         return -1;
 169     }
 170 
 171     node1_weight = node1->weight;
 172     node2_weight = node2->weight;
 173 
 174     if (can_run_resources(node1) == FALSE) {
 175         node1_weight = -INFINITY;
 176     }
 177     if (can_run_resources(node2) == FALSE) {
 178         node2_weight = -INFINITY;
 179     }
 180 
 181     if (node1_weight > node2_weight) {
 182         crm_trace("%s (%d) > %s (%d) : weight",
 183                   node1->details->uname, node1_weight, node2->details->uname, node2_weight);
 184         return -1;
 185     }
 186 
 187     if (node1_weight < node2_weight) {
 188         crm_trace("%s (%d) < %s (%d) : weight",
 189                   node1->details->uname, node1_weight, node2->details->uname, node2_weight);
 190         return 1;
 191     }
 192 
 193     crm_trace("%s (%d) == %s (%d) : weight",
 194               node1->details->uname, node1_weight, node2->details->uname, node2_weight);
 195 
 196     if (pcmk__str_eq(nw->data_set->placement_strategy, "minimal", pcmk__str_casei)) {
 197         goto equal;
 198     }
 199 
 200     if (pcmk__str_eq(nw->data_set->placement_strategy, "balanced", pcmk__str_casei)) {
 201         result = compare_capacity(node1, node2);
 202         if (result < 0) {
 203             crm_trace("%s > %s : capacity (%d)",
 204                       node1->details->uname, node2->details->uname, result);
 205             return -1;
 206         } else if (result > 0) {
 207             crm_trace("%s < %s : capacity (%d)",
 208                       node1->details->uname, node2->details->uname, result);
 209             return 1;
 210         }
 211     }
 212 
 213     /* now try to balance resources across the cluster */
 214     if (node1->details->num_resources < node2->details->num_resources) {
 215         crm_trace("%s (%d) > %s (%d) : resources",
 216                   node1->details->uname, node1->details->num_resources,
 217                   node2->details->uname, node2->details->num_resources);
 218         return -1;
 219 
 220     } else if (node1->details->num_resources > node2->details->num_resources) {
 221         crm_trace("%s (%d) < %s (%d) : resources",
 222                   node1->details->uname, node1->details->num_resources,
 223                   node2->details->uname, node2->details->num_resources);
 224         return 1;
 225     }
 226 
 227     if (nw->active && nw->active->details == node1->details) {
 228         crm_trace("%s (%d) > %s (%d) : active",
 229                   node1->details->uname, node1->details->num_resources,
 230                   node2->details->uname, node2->details->num_resources);
 231         return -1;
 232     } else if (nw->active && nw->active->details == node2->details) {
 233         crm_trace("%s (%d) < %s (%d) : active",
 234                   node1->details->uname, node1->details->num_resources,
 235                   node2->details->uname, node2->details->num_resources);
 236         return 1;
 237     }
 238   equal:
 239     crm_trace("%s = %s", node1->details->uname, node2->details->uname);
 240     return strcmp(node1->details->uname, node2->details->uname);
 241 }
 242 
 243 GList *
 244 sort_nodes_by_weight(GList *nodes, pe_node_t *active_node,
     /* [previous][next][first][last][top][bottom][index][help] */
 245                      pe_working_set_t *data_set)
 246 {
 247     struct node_weight_s nw = { active_node, data_set };
 248 
 249     return g_list_sort_with_data(nodes, sort_node_weight, &nw);
 250 }
 251 
 252 void
 253 native_deallocate(pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 254 {
 255     if (rsc->allocated_to) {
 256         pe_node_t *old = rsc->allocated_to;
 257 
 258         crm_info("Deallocating %s from %s", rsc->id, old->details->uname);
 259         pe__set_resource_flags(rsc, pe_rsc_provisional);
 260         rsc->allocated_to = NULL;
 261 
 262         old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc, rsc);
 263         old->details->num_resources--;
 264         /* old->count--; */
 265         calculate_utilization(old->details->utilization, rsc->utilization, TRUE);
 266         free(old);
 267     }
 268 }
 269 
 270 gboolean
 271 native_assign_node(pe_resource_t *rsc, pe_node_t *chosen, gboolean force)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273     pcmk__output_t *out = rsc->cluster->priv;
 274 
 275     CRM_ASSERT(rsc->variant == pe_native);
 276 
 277     if (force == FALSE && chosen != NULL) {
 278         bool unset = FALSE;
 279 
 280         if(chosen->weight < 0) {
 281             unset = TRUE;
 282 
 283             // Allow the graph to assume that the remote resource will come up
 284         } else if (!can_run_resources(chosen) && !pe__is_guest_node(chosen)) {
 285             unset = TRUE;
 286         }
 287 
 288         if(unset) {
 289             crm_debug("All nodes for resource %s are unavailable"
 290                       ", unclean or shutting down (%s: %d, %d)",
 291                       rsc->id, chosen->details->uname, can_run_resources(chosen), chosen->weight);
 292             pe__set_next_role(rsc, RSC_ROLE_STOPPED, "node availability");
 293             chosen = NULL;
 294         }
 295     }
 296 
 297     /* todo: update the old node for each resource to reflect its
 298      * new resource count
 299      */
 300 
 301     native_deallocate(rsc);
 302     pe__clear_resource_flags(rsc, pe_rsc_provisional);
 303 
 304     if (chosen == NULL) {
 305         GList *gIter = NULL;
 306         char *rc_inactive = pcmk__itoa(PCMK_OCF_NOT_RUNNING);
 307 
 308         crm_debug("Could not allocate a node for %s", rsc->id);
 309         pe__set_next_role(rsc, RSC_ROLE_STOPPED, "unable to allocate");
 310 
 311         for (gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
 312             pe_action_t *op = (pe_action_t *) gIter->data;
 313             const char *interval_ms_s = g_hash_table_lookup(op->meta, XML_LRM_ATTR_INTERVAL_MS);
 314 
 315             crm_debug("Processing %s", op->uuid);
 316             if(pcmk__str_eq(RSC_STOP, op->task, pcmk__str_casei)) {
 317                 update_action_flags(op, pe_action_optional | pe_action_clear,
 318                                     __func__, __LINE__);
 319 
 320             } else if(pcmk__str_eq(RSC_START, op->task, pcmk__str_casei)) {
 321                 update_action_flags(op, pe_action_runnable | pe_action_clear,
 322                                     __func__, __LINE__);
 323                 //pe__set_resource_flags(rsc, pe_rsc_block);
 324 
 325             } else if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
 326                 if(pcmk__str_eq(rc_inactive, g_hash_table_lookup(op->meta, XML_ATTR_TE_TARGET_RC), pcmk__str_casei)) {
 327                     /* This is a recurring monitor for the stopped state, leave it alone */
 328 
 329                 } else {
 330                     /* Normal monitor operation, cancel it */
 331                     update_action_flags(op, pe_action_runnable | pe_action_clear,
 332                                         __func__, __LINE__);
 333                 }
 334             }
 335         }
 336 
 337         free(rc_inactive);
 338         return FALSE;
 339     }
 340 
 341     crm_debug("Assigning %s to %s", chosen->details->uname, rsc->id);
 342     rsc->allocated_to = pe__copy_node(chosen);
 343 
 344     chosen->details->allocated_rsc = g_list_prepend(chosen->details->allocated_rsc, rsc);
 345     chosen->details->num_resources++;
 346     chosen->count++;
 347     calculate_utilization(chosen->details->utilization, rsc->utilization, FALSE);
 348 
 349     if (pcmk_is_set(rsc->cluster->flags, pe_flag_show_utilization)) {
 350         out->message(out, "resource-util", rsc, chosen, __func__);
 351     }
 352 
 353     return TRUE;
 354 }
 355 
 356 void
 357 log_action(unsigned int log_level, const char *pre_text, pe_action_t * action, gboolean details)
     /* [previous][next][first][last][top][bottom][index][help] */
 358 {
 359     const char *node_uname = NULL;
 360     const char *node_uuid = NULL;
 361     const char *desc = NULL;
 362 
 363     if (action == NULL) {
 364         crm_trace("%s%s: <NULL>", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
 365         return;
 366     }
 367 
 368     if (pcmk_is_set(action->flags, pe_action_pseudo)) {
 369         node_uname = NULL;
 370         node_uuid = NULL;
 371 
 372     } else if (action->node != NULL) {
 373         node_uname = action->node->details->uname;
 374         node_uuid = action->node->details->id;
 375     } else {
 376         node_uname = "<none>";
 377         node_uuid = NULL;
 378     }
 379 
 380     switch (text2task(action->task)) {
 381         case stonith_node:
 382         case shutdown_crm:
 383             if (pcmk_is_set(action->flags, pe_action_pseudo)) {
 384                 desc = "Pseudo ";
 385             } else if (pcmk_is_set(action->flags, pe_action_optional)) {
 386                 desc = "Optional ";
 387             } else if (!pcmk_is_set(action->flags, pe_action_runnable)) {
 388                 desc = "!!Non-Startable!! ";
 389             } else if (pcmk_is_set(action->flags, pe_action_processed)) {
 390                desc = "";
 391             } else {
 392                desc = "(Provisional) ";
 393             }
 394             crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
 395                       ((pre_text == NULL)? "" : pre_text),
 396                       ((pre_text == NULL)? "" : ": "),
 397                       desc, action->id, action->uuid,
 398                       (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
 399                       (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
 400                       (node_uuid? ")" : ""));
 401             break;
 402         default:
 403             if (pcmk_is_set(action->flags, pe_action_optional)) {
 404                 desc = "Optional ";
 405             } else if (pcmk_is_set(action->flags, pe_action_pseudo)) {
 406                 desc = "Pseudo ";
 407             } else if (!pcmk_is_set(action->flags, pe_action_runnable)) {
 408                 desc = "!!Non-Startable!! ";
 409             } else if (pcmk_is_set(action->flags, pe_action_processed)) {
 410                desc = "";
 411             } else {
 412                desc = "(Provisional) ";
 413             }
 414             crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
 415                       ((pre_text == NULL)? "" : pre_text),
 416                       ((pre_text == NULL)? "" : ": "),
 417                       desc, action->id, action->uuid,
 418                       (action->rsc? action->rsc->id : "<none>"),
 419                       (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
 420                       (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
 421                       (node_uuid? ")" : ""));
 422             break;
 423     }
 424 
 425     if (details) {
 426         GList *gIter = NULL;
 427 
 428         crm_trace("\t\t====== Preceding Actions");
 429 
 430         gIter = action->actions_before;
 431         for (; gIter != NULL; gIter = gIter->next) {
 432             pe_action_wrapper_t *other = (pe_action_wrapper_t *) gIter->data;
 433 
 434             log_action(log_level + 1, "\t\t", other->action, FALSE);
 435         }
 436 
 437         crm_trace("\t\t====== Subsequent Actions");
 438 
 439         gIter = action->actions_after;
 440         for (; gIter != NULL; gIter = gIter->next) {
 441             pe_action_wrapper_t *other = (pe_action_wrapper_t *) gIter->data;
 442 
 443             log_action(log_level + 1, "\t\t", other->action, FALSE);
 444         }
 445 
 446         crm_trace("\t\t====== End");
 447 
 448     } else {
 449         crm_trace("\t\t(before=%d, after=%d)",
 450                   g_list_length(action->actions_before), g_list_length(action->actions_after));
 451     }
 452 }
 453 
 454 gboolean
 455 can_run_any(GHashTable * nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 456 {
 457     GHashTableIter iter;
 458     pe_node_t *node = NULL;
 459 
 460     if (nodes == NULL) {
 461         return FALSE;
 462     }
 463 
 464     g_hash_table_iter_init(&iter, nodes);
 465     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 466         if (can_run_resources(node) && node->weight >= 0) {
 467             return TRUE;
 468         }
 469     }
 470 
 471     return FALSE;
 472 }
 473 
 474 pe_action_t *
 475 create_pseudo_resource_op(pe_resource_t * rsc, const char *task, bool optional, bool runnable, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 476 {
 477     pe_action_t *action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0),
 478                                         task, NULL, optional, TRUE, data_set);
 479     update_action_flags(action, pe_action_pseudo, __func__, __LINE__);
 480     update_action_flags(action, pe_action_runnable, __func__, __LINE__);
 481     if(runnable) {
 482         update_action_flags(action, pe_action_runnable, __func__, __LINE__);
 483     }
 484     return action;
 485 }
 486 
 487 /*!
 488  * \internal
 489  * \brief Create an executor cancel op
 490  *
 491  * \param[in] rsc          Resource of action to cancel
 492  * \param[in] task         Name of action to cancel
 493  * \param[in] interval_ms  Interval of action to cancel
 494  * \param[in] node         Node of action to cancel
 495  * \param[in] data_set     Working set of cluster
 496  *
 497  * \return Created op
 498  */
 499 pe_action_t *
 500 pe_cancel_op(pe_resource_t *rsc, const char *task, guint interval_ms,
     /* [previous][next][first][last][top][bottom][index][help] */
 501              pe_node_t *node, pe_working_set_t *data_set)
 502 {
 503     pe_action_t *cancel_op;
 504     char *interval_ms_s = crm_strdup_printf("%u", interval_ms);
 505 
 506     // @TODO dangerous if possible to schedule another action with this key
 507     char *key = pcmk__op_key(rsc->id, task, interval_ms);
 508 
 509     cancel_op = custom_action(rsc, key, RSC_CANCEL, node, FALSE, TRUE,
 510                               data_set);
 511 
 512     free(cancel_op->task);
 513     cancel_op->task = strdup(RSC_CANCEL);
 514 
 515     free(cancel_op->cancel_task);
 516     cancel_op->cancel_task = strdup(task);
 517 
 518     add_hash_param(cancel_op->meta, XML_LRM_ATTR_TASK, task);
 519     add_hash_param(cancel_op->meta, XML_LRM_ATTR_INTERVAL_MS, interval_ms_s);
 520     free(interval_ms_s);
 521 
 522     return cancel_op;
 523 }
 524 
 525 /*!
 526  * \internal
 527  * \brief Create a shutdown op for a scheduler transition
 528  *
 529  * \param[in] node         Node being shut down
 530  * \param[in] data_set     Working set of cluster
 531  *
 532  * \return Created op
 533  */
 534 pe_action_t *
 535 sched_shutdown_op(pe_node_t *node, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 536 {
 537     char *shutdown_id = crm_strdup_printf("%s-%s", CRM_OP_SHUTDOWN,
 538                                           node->details->uname);
 539 
 540     pe_action_t *shutdown_op = custom_action(NULL, shutdown_id, CRM_OP_SHUTDOWN,
 541                                              node, FALSE, TRUE, data_set);
 542 
 543     crm_notice("Scheduling shutdown of node %s", node->details->uname);
 544     shutdown_constraints(node, shutdown_op, data_set);
 545     add_hash_param(shutdown_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
 546     return shutdown_op;
 547 }
 548 
 549 static char *
 550 generate_transition_magic(const char *transition_key, int op_status, int op_rc)
     /* [previous][next][first][last][top][bottom][index][help] */
 551 {
 552     CRM_CHECK(transition_key != NULL, return NULL);
 553     return crm_strdup_printf("%d:%d;%s", op_status, op_rc, transition_key);
 554 }
 555 
 556 static void
 557 append_digest(lrmd_event_data_t *op, xmlNode *update, const char *version,
     /* [previous][next][first][last][top][bottom][index][help] */
 558               const char *magic, int level)
 559 {
 560     /* this will enable us to later determine that the
 561      *   resource's parameters have changed and we should force
 562      *   a restart
 563      */
 564     char *digest = NULL;
 565     xmlNode *args_xml = NULL;
 566 
 567     if (op->params == NULL) {
 568         return;
 569     }
 570 
 571     args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
 572     g_hash_table_foreach(op->params, hash2field, args_xml);
 573     pcmk__filter_op_for_digest(args_xml);
 574     digest = calculate_operation_digest(args_xml, version);
 575 
 576 #if 0
 577     if (level < get_crm_log_level()
 578         && op->interval_ms == 0 && pcmk__str_eq(op->op_type, CRMD_ACTION_START, pcmk__str_none)) {
 579         char *digest_source = dump_xml_unformatted(args_xml);
 580 
 581         do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n",
 582                    digest, ID(update), magic, digest_source);
 583         free(digest_source);
 584     }
 585 #endif
 586     crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
 587 
 588     free_xml(args_xml);
 589     free(digest);
 590 }
 591 
 592 #define FAKE_TE_ID     "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 593 
 594 /*!
 595  * \internal
 596  * \brief Create XML for resource operation history update
 597  *
 598  * \param[in,out] parent          Parent XML node to add to
 599  * \param[in,out] op              Operation event data
 600  * \param[in]     caller_version  DC feature set
 601  * \param[in]     target_rc       Expected result of operation
 602  * \param[in]     node            Name of node on which operation was performed
 603  * \param[in]     origin          Arbitrary description of update source
 604  * \param[in]     level           A log message will be logged at this level
 605  *
 606  * \return Newly created XML node for history update
 607  */
 608 xmlNode *
 609 pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 610                          const char *caller_version, int target_rc,
 611                          const char *node, const char *origin, int level)
 612 {
 613     char *key = NULL;
 614     char *magic = NULL;
 615     char *op_id = NULL;
 616     char *op_id_additional = NULL;
 617     char *local_user_data = NULL;
 618     const char *exit_reason = NULL;
 619 
 620     xmlNode *xml_op = NULL;
 621     const char *task = NULL;
 622 
 623     CRM_CHECK(op != NULL, return NULL);
 624     do_crm_log(level, "%s: Updating resource %s after %s op %s (interval=%u)",
 625                origin, op->rsc_id, op->op_type, services_lrm_status_str(op->op_status),
 626                op->interval_ms);
 627 
 628     crm_trace("DC version: %s", caller_version);
 629 
 630     task = op->op_type;
 631 
 632     /* Record a successful agent reload as a start, and a failed one as a
 633      * monitor, to make life easier for the scheduler when determining the
 634      * current state.
 635      *
 636      * @COMPAT We should check "reload" here only if the operation was for a
 637      * pre-OCF-1.1 resource agent, but we don't know that here, and we should
 638      * only ever get results for actions scheduled by us, so we can reasonably
 639      * assume any "reload" is actually a pre-1.1 agent reload.
 640      */
 641     if (pcmk__str_any_of(task, CRMD_ACTION_RELOAD, CRMD_ACTION_RELOAD_AGENT,
 642                          NULL)) {
 643         if (op->op_status == PCMK_LRM_OP_DONE) {
 644             task = CRMD_ACTION_START;
 645         } else {
 646             task = CRMD_ACTION_STATUS;
 647         }
 648     }
 649 
 650     key = pcmk__op_key(op->rsc_id, task, op->interval_ms);
 651     if (pcmk__str_eq(task, CRMD_ACTION_NOTIFY, pcmk__str_none)) {
 652         const char *n_type = crm_meta_value(op->params, "notify_type");
 653         const char *n_task = crm_meta_value(op->params, "notify_operation");
 654 
 655         CRM_LOG_ASSERT(n_type != NULL);
 656         CRM_LOG_ASSERT(n_task != NULL);
 657         op_id = pcmk__notify_key(op->rsc_id, n_type, n_task);
 658 
 659         if (op->op_status != PCMK_LRM_OP_PENDING) {
 660             /* Ignore notify errors.
 661              *
 662              * @TODO It might be better to keep the correct result here, and
 663              * ignore it in process_graph_event().
 664              */
 665             op->op_status = PCMK_LRM_OP_DONE;
 666             op->rc = 0;
 667         }
 668 
 669     } else if (did_rsc_op_fail(op, target_rc)) {
 670         op_id = pcmk__op_key(op->rsc_id, "last_failure", 0);
 671         if (op->interval_ms == 0) {
 672             // Ensure 'last' gets updated, in case record-pending is true
 673             op_id_additional = pcmk__op_key(op->rsc_id, "last", 0);
 674         }
 675         exit_reason = op->exit_reason;
 676 
 677     } else if (op->interval_ms > 0) {
 678         op_id = strdup(key);
 679 
 680     } else {
 681         op_id = pcmk__op_key(op->rsc_id, "last", 0);
 682     }
 683 
 684   again:
 685     xml_op = pcmk__xe_match(parent, XML_LRM_TAG_RSC_OP, XML_ATTR_ID, op_id);
 686     if (xml_op == NULL) {
 687         xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP);
 688     }
 689 
 690     if (op->user_data == NULL) {
 691         crm_debug("Generating fake transition key for: " PCMK__OP_FMT
 692                   " %d from %s", op->rsc_id, op->op_type, op->interval_ms,
 693                   op->call_id, origin);
 694         local_user_data = pcmk__transition_key(-1, op->call_id, target_rc,
 695                                                FAKE_TE_ID);
 696         op->user_data = local_user_data;
 697     }
 698 
 699     if(magic == NULL) {
 700         magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
 701     }
 702 
 703     crm_xml_add(xml_op, XML_ATTR_ID, op_id);
 704     crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
 705     crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
 706     crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
 707     crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
 708     crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
 709     crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
 710     crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason == NULL ? "" : exit_reason);
 711     crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */
 712 
 713     crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
 714     crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
 715     crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status);
 716     crm_xml_add_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, op->interval_ms);
 717 
 718     if (compare_version("2.1", caller_version) <= 0) {
 719         if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
 720             crm_trace("Timing data (" PCMK__OP_FMT
 721                       "): last=%u change=%u exec=%u queue=%u",
 722                       op->rsc_id, op->op_type, op->interval_ms,
 723                       op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
 724 
 725             if ((op->interval_ms != 0) && (op->t_rcchange != 0)) {
 726                 // Recurring ops may have changed rc after initial run
 727                 crm_xml_add_ll(xml_op, XML_RSC_OP_LAST_CHANGE,
 728                                (long long) op->t_rcchange);
 729             } else {
 730                 crm_xml_add_ll(xml_op, XML_RSC_OP_LAST_CHANGE,
 731                                (long long) op->t_run);
 732             }
 733 
 734             crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time);
 735             crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time);
 736         }
 737     }
 738 
 739     if (pcmk__str_any_of(op->op_type, CRMD_ACTION_MIGRATE, CRMD_ACTION_MIGRATED, NULL)) {
 740         /*
 741          * Record migrate_source and migrate_target always for migrate ops.
 742          */
 743         const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
 744 
 745         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
 746 
 747         name = XML_LRM_ATTR_MIGRATE_TARGET;
 748         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
 749     }
 750 
 751     append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
 752 
 753     if (op_id_additional) {
 754         free(op_id);
 755         op_id = op_id_additional;
 756         op_id_additional = NULL;
 757         goto again;
 758     }
 759 
 760     if (local_user_data) {
 761         free(local_user_data);
 762         op->user_data = NULL;
 763     }
 764     free(magic);
 765     free(op_id);
 766     free(key);
 767     return xml_op;
 768 }
 769 
 770 pcmk__output_t *
 771 pcmk__new_logger(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 772 {
 773     int rc = pcmk_rc_ok;
 774     pcmk__output_t *out = NULL;
 775     const char* argv[] = { "", NULL };
 776     pcmk__supported_format_t formats[] = {
 777         PCMK__SUPPORTED_FORMAT_LOG,
 778         { NULL, NULL, NULL }
 779     };
 780 
 781     pcmk__register_formats(NULL, formats);
 782     rc = pcmk__output_new(&out, "log", NULL, (char**)argv);
 783     if ((rc != pcmk_rc_ok) || (out == NULL)) {
 784         crm_err("Can't log resource details due to internal error: %s\n",
 785                 pcmk_rc_str(rc));
 786         return NULL;
 787     }
 788 
 789     pe__register_messages(out);
 790     pcmk__register_lib_messages(out);
 791     return out;
 792 }

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