root/lib/pengine/pe_notif.c

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

DEFINITIONS

This source file includes following definitions.
  1. compare_notify_entries
  2. dup_notify_entry
  3. get_node_names
  4. notify_entries_to_strings
  5. copy_meta_to_notify
  6. add_notify_data_to_action_meta
  7. new_notify_pseudo_action
  8. new_notify_action
  9. new_post_notify_action
  10. pe__action_notif_pseudo_ops
  11. new_notify_entry
  12. collect_resource_data
  13. add_notif_keys
  14. find_remote_start
  15. create_notify_actions
  16. pe__create_action_notifications
  17. pe__free_action_notification_data
  18. pe__order_notifs_after_fencing

   1 /*
   2  * Copyright 2004-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 #include <crm/common/xml.h>
  12 
  13 #include <crm/pengine/internal.h>
  14 #include <pacemaker-internal.h>
  15 
  16 #include "pe_status_private.h"
  17 
  18 typedef struct notify_entry_s {
  19     const pcmk_resource_t *rsc;
  20     const pcmk_node_t *node;
  21 } notify_entry_t;
  22 
  23 /*!
  24  * \internal
  25  * \brief Compare two notification entries
  26  *
  27  * Compare two notification entries, where the one with the alphabetically first
  28  * resource name (or if equal, node ID) sorts as first, with NULL sorting as
  29  * less than non-NULL.
  30  *
  31  * \param[in] a  First notification entry to compare
  32  * \param[in] b  Second notification entry to compare
  33  *
  34  * \return -1 if \p a sorts before \p b, 0 if they are equal, otherwise 1
  35  */
  36 static gint
  37 compare_notify_entries(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39     int tmp;
  40     const notify_entry_t *entry_a = a;
  41     const notify_entry_t *entry_b = b;
  42 
  43     // NULL a or b is not actually possible
  44     if ((entry_a == NULL) && (entry_b == NULL)) {
  45         return 0;
  46     }
  47     if (entry_a == NULL) {
  48         return 1;
  49     }
  50     if (entry_b == NULL) {
  51         return -1;
  52     }
  53 
  54     // NULL resources sort first
  55     if ((entry_a->rsc == NULL) && (entry_b->rsc == NULL)) {
  56         return 0;
  57     }
  58     if (entry_a->rsc == NULL) {
  59         return 1;
  60     }
  61     if (entry_b->rsc == NULL) {
  62         return -1;
  63     }
  64 
  65     // Compare resource names
  66     tmp = strcmp(entry_a->rsc->id, entry_b->rsc->id);
  67     if (tmp != 0) {
  68         return tmp;
  69     }
  70 
  71     // Otherwise NULL nodes sort first
  72     if ((entry_a->node == NULL) && (entry_b->node == NULL)) {
  73         return 0;
  74     }
  75     if (entry_a->node == NULL) {
  76         return 1;
  77     }
  78     if (entry_b->node == NULL) {
  79         return -1;
  80     }
  81 
  82     // Finally, compare node IDs
  83     return strcmp(entry_a->node->priv->id, entry_b->node->priv->id);
  84 }
  85 
  86 /*!
  87  * \internal
  88  * \brief Duplicate a notification entry
  89  *
  90  * \param[in] entry  Entry to duplicate
  91  *
  92  * \return Newly allocated duplicate of \p entry
  93  * \note It is the caller's responsibility to free the return value.
  94  */
  95 static notify_entry_t *
  96 dup_notify_entry(const notify_entry_t *entry)
     /* [previous][next][first][last][top][bottom][index][help] */
  97 {
  98     notify_entry_t *dup = pcmk__assert_alloc(1, sizeof(notify_entry_t));
  99 
 100     dup->rsc = entry->rsc;
 101     dup->node = entry->node;
 102     return dup;
 103 }
 104 
 105 /*!
 106  * \internal
 107  * \brief Given a list of nodes, create strings with node names
 108  *
 109  * \param[in]  list             List of nodes (as pcmk_node_t *)
 110  * \param[out] all_node_names   If not NULL, will be set to space-separated list
 111  *                              of the names of all nodes in \p list
 112  * \param[out] host_node_names  Same as \p all_node_names, except active
 113  *                              guest nodes will list the name of their host
 114  *
 115  * \note The caller is responsible for freeing the output argument values using
 116  *       \p g_string_free().
 117  */
 118 static void
 119 get_node_names(const GList *list, GString **all_node_names,
     /* [previous][next][first][last][top][bottom][index][help] */
 120                GString **host_node_names)
 121 {
 122     if (all_node_names != NULL) {
 123         *all_node_names = NULL;
 124     }
 125     if (host_node_names != NULL) {
 126         *host_node_names = NULL;
 127     }
 128 
 129     for (const GList *iter = list; iter != NULL; iter = iter->next) {
 130         const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
 131 
 132         if (node->priv->name == NULL) {
 133             continue;
 134         }
 135 
 136         // Always add to list of all node names
 137         if (all_node_names != NULL) {
 138             pcmk__add_word(all_node_names, 1024, node->priv->name);
 139         }
 140 
 141         // Add to host node name list if appropriate
 142         if (host_node_names != NULL) {
 143             if (pcmk__is_guest_or_bundle_node(node)) {
 144                 const pcmk_resource_t *launcher = NULL;
 145 
 146                 launcher = node->priv->remote->priv->launcher;
 147                 if (launcher->priv->active_nodes != NULL) {
 148                     node = pcmk__current_node(launcher);
 149                     if (node->priv->name == NULL) {
 150                         continue;
 151                     }
 152                 }
 153             }
 154             pcmk__add_word(host_node_names, 1024, node->priv->name);
 155         }
 156     }
 157 
 158     if ((all_node_names != NULL) && (*all_node_names == NULL)) {
 159         *all_node_names = g_string_new(" ");
 160     }
 161     if ((host_node_names != NULL) && (*host_node_names == NULL)) {
 162         *host_node_names = g_string_new(" ");
 163     }
 164 }
 165 
 166 /*!
 167  * \internal
 168  * \brief Create strings of instance and node names from notification entries
 169  *
 170  * \param[in,out] list        List of notification entries (will be sorted here)
 171  * \param[out]    rsc_names   If not NULL, will be set to space-separated list
 172  *                            of clone instances from \p list
 173  * \param[out]    node_names  If not NULL, will be set to space-separated list
 174  *                            of node names from \p list
 175  *
 176  * \return (Possibly new) head of sorted \p list
 177  * \note The caller is responsible for freeing the output argument values using
 178  *       \p g_list_free_full() and \p g_string_free().
 179  */
 180 static GList *
 181 notify_entries_to_strings(GList *list, GString **rsc_names,
     /* [previous][next][first][last][top][bottom][index][help] */
 182                           GString **node_names)
 183 {
 184     const char *last_rsc_id = NULL;
 185 
 186     // Initialize output lists to NULL
 187     if (rsc_names != NULL) {
 188         *rsc_names = NULL;
 189     }
 190     if (node_names != NULL) {
 191         *node_names = NULL;
 192     }
 193 
 194     // Sort input list for user-friendliness (and ease of filtering duplicates)
 195     list = g_list_sort(list, compare_notify_entries);
 196 
 197     for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
 198         notify_entry_t *entry = (notify_entry_t *) gIter->data;
 199 
 200         // Entry must have a resource (with ID)
 201         CRM_LOG_ASSERT((entry != NULL) && (entry->rsc != NULL)
 202                        && (entry->rsc->id != NULL));
 203         if ((entry == NULL) || (entry->rsc == NULL)
 204             || (entry->rsc->id == NULL)) {
 205             continue;
 206         }
 207 
 208         // Entry must have a node unless listing inactive resources
 209         CRM_LOG_ASSERT((node_names == NULL) || (entry->node != NULL));
 210         if ((node_names != NULL) && (entry->node == NULL)) {
 211             continue;
 212         }
 213 
 214         // Don't add duplicates of a particular clone instance
 215         if (pcmk__str_eq(entry->rsc->id, last_rsc_id, pcmk__str_none)) {
 216             continue;
 217         }
 218         last_rsc_id = entry->rsc->id;
 219 
 220         if (rsc_names != NULL) {
 221             pcmk__add_word(rsc_names, 1024, entry->rsc->id);
 222         }
 223         if ((node_names != NULL) && (entry->node->priv->name != NULL)) {
 224             pcmk__add_word(node_names, 1024, entry->node->priv->name);
 225         }
 226     }
 227 
 228     // If there are no entries, return "empty" lists
 229     if ((rsc_names != NULL) && (*rsc_names == NULL)) {
 230         *rsc_names = g_string_new(" ");
 231     }
 232     if ((node_names != NULL) && (*node_names == NULL)) {
 233         *node_names = g_string_new(" ");
 234     }
 235 
 236     return list;
 237 }
 238 
 239 /*!
 240  * \internal
 241  * \brief Copy a meta-attribute into a notify action
 242  *
 243  * \param[in]     key        Name of meta-attribute to copy
 244  * \param[in]     value      Value of meta-attribute to copy
 245  * \param[in,out] user_data  Notify action to copy into
 246  */
 247 static void
 248 copy_meta_to_notify(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 249 {
 250     pcmk_action_t *notify = (pcmk_action_t *) user_data;
 251 
 252     /* Any existing meta-attributes (for example, the action timeout) are for
 253      * the notify action itself, so don't override those.
 254      */
 255     if (g_hash_table_lookup(notify->meta, (const char *) key) != NULL) {
 256         return;
 257     }
 258 
 259     pcmk__insert_dup(notify->meta, (const char *) key, (const char *) value);
 260 }
 261 
 262 static void
 263 add_notify_data_to_action_meta(const notify_data_t *n_data,
     /* [previous][next][first][last][top][bottom][index][help] */
 264                                pcmk_action_t *action)
 265 {
 266     for (const GSList *item = n_data->keys; item; item = item->next) {
 267         const pcmk_nvpair_t *nvpair = (const pcmk_nvpair_t *) item->data;
 268 
 269         pcmk__insert_meta(action, nvpair->name, nvpair->value);
 270     }
 271 }
 272 
 273 /*!
 274  * \internal
 275  * \brief Create a new notify pseudo-action for a clone resource
 276  *
 277  * \param[in,out] rsc           Clone resource that notification is for
 278  * \param[in]     action        Action to use in notify action key
 279  * \param[in]     notif_action  PCMK_ACTION_NOTIFY or PCMK_ACTION_NOTIFIED
 280  * \param[in]     notif_type    "pre", "post", "confirmed-pre", "confirmed-post"
 281  *
 282  * \return Newly created notify pseudo-action
 283  */
 284 static pcmk_action_t *
 285 new_notify_pseudo_action(pcmk_resource_t *rsc, const pcmk_action_t *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 286                          const char *notif_action, const char *notif_type)
 287 {
 288     pcmk_action_t *notify = NULL;
 289 
 290     notify = custom_action(rsc,
 291                            pcmk__notify_key(rsc->id, notif_type, action->task),
 292                            notif_action, NULL,
 293                            pcmk_is_set(action->flags, pcmk__action_optional),
 294                            rsc->priv->scheduler);
 295     pcmk__set_action_flags(notify, pcmk__action_pseudo);
 296     pcmk__insert_meta(notify, "notify_key_type", notif_type);
 297     pcmk__insert_meta(notify, "notify_key_operation", action->task);
 298     return notify;
 299 }
 300 
 301 /*!
 302  * \internal
 303  * \brief Create a new notify action for a clone instance
 304  *
 305  * \param[in,out] rsc          Clone instance that notification is for
 306  * \param[in]     node         Node that notification is for
 307  * \param[in,out] op           Action that notification is for
 308  * \param[in,out] notify_done  Parent pseudo-action for notifications complete
 309  * \param[in]     n_data       Notification values to add to action meta-data
 310  *
 311  * \return Newly created notify action
 312  */
 313 static pcmk_action_t *
 314 new_notify_action(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 315                   pcmk_action_t *op, pcmk_action_t *notify_done,
 316                   const notify_data_t *n_data)
 317 {
 318     char *key = NULL;
 319     pcmk_action_t *notify_action = NULL;
 320     const char *value = NULL;
 321     const char *task = NULL;
 322     const char *skip_reason = NULL;
 323 
 324     CRM_CHECK((rsc != NULL) && (node != NULL), return NULL);
 325 
 326     // Ensure we have all the info we need
 327     if (op == NULL) {
 328         skip_reason = "no action";
 329     } else if (notify_done == NULL) {
 330         skip_reason = "no parent notification";
 331     } else if (!node->details->online) {
 332         skip_reason = "node offline";
 333     } else if (!pcmk_is_set(op->flags, pcmk__action_runnable)) {
 334         skip_reason = "original action not runnable";
 335     }
 336     if (skip_reason != NULL) {
 337         pcmk__rsc_trace(rsc, "Skipping notify action for %s on %s: %s",
 338                         rsc->id, pcmk__node_name(node), skip_reason);
 339         return NULL;
 340     }
 341 
 342     value = g_hash_table_lookup(op->meta, "notify_type");     // "pre" or "post"
 343     task = g_hash_table_lookup(op->meta, "notify_operation"); // original action
 344 
 345     pcmk__rsc_trace(rsc, "Creating notify action for %s on %s (%s-%s)",
 346                     rsc->id, pcmk__node_name(node), value, task);
 347 
 348     // Create the notify action
 349     key = pcmk__notify_key(rsc->id, value, task);
 350     notify_action = custom_action(rsc, key, op->task, node,
 351                                   pcmk_is_set(op->flags, pcmk__action_optional),
 352                                   rsc->priv->scheduler);
 353 
 354     // Add meta-data to notify action
 355     g_hash_table_foreach(op->meta, copy_meta_to_notify, notify_action);
 356     add_notify_data_to_action_meta(n_data, notify_action);
 357 
 358     // Order notify after original action and before parent notification
 359     order_actions(op, notify_action, pcmk__ar_ordered);
 360     order_actions(notify_action, notify_done, pcmk__ar_ordered);
 361     return notify_action;
 362 }
 363 
 364 /*!
 365  * \internal
 366  * \brief Create a new "post-" notify action for a clone instance
 367  *
 368  * \param[in,out] rsc     Clone instance that notification is for
 369  * \param[in]     node    Node that notification is for
 370  * \param[in,out] n_data  Notification values to add to action meta-data
 371  */
 372 static void
 373 new_post_notify_action(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 374                        notify_data_t *n_data)
 375 {
 376     pcmk_action_t *notify = NULL;
 377 
 378     pcmk__assert(n_data != NULL);
 379 
 380     // Create the "post-" notify action for specified instance
 381     notify = new_notify_action(rsc, node, n_data->post, n_data->post_done,
 382                                n_data);
 383     if (notify != NULL) {
 384         notify->priority = PCMK_SCORE_INFINITY;
 385     }
 386 
 387     // Order recurring monitors after all "post-" notifications complete
 388     if (n_data->post_done == NULL) {
 389         return;
 390     }
 391     for (GList *iter = rsc->priv->actions; iter != NULL; iter = iter->next) {
 392         pcmk_action_t *mon = (pcmk_action_t *) iter->data;
 393         const char *interval_ms_s = NULL;
 394 
 395         interval_ms_s = g_hash_table_lookup(mon->meta, PCMK_META_INTERVAL);
 396         if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)
 397             || pcmk__str_eq(mon->task, PCMK_ACTION_CANCEL, pcmk__str_none)) {
 398             continue; // Not a recurring monitor
 399         }
 400         order_actions(n_data->post_done, mon, pcmk__ar_ordered);
 401     }
 402 }
 403 
 404 /*!
 405  * \internal
 406  * \brief Create and order notification pseudo-actions for a clone action
 407  *
 408  * In addition to the actual notify actions needed for each clone instance,
 409  * clone notifications also require pseudo-actions to provide ordering points
 410  * in the notification process. This creates the notification data, along with
 411  * appropriate pseudo-actions and their orderings.
 412  *
 413  * For example, the ordering sequence for starting a clone is:
 414  *
 415  *     "pre-" notify pseudo-action for clone
 416  *     -> "pre-" notify actions for each clone instance
 417  *     -> "pre-" notifications complete pseudo-action for clone
 418  *     -> start actions for each clone instance
 419  *     -> "started" pseudo-action for clone
 420  *     -> "post-" notify pseudo-action for clone
 421  *     -> "post-" notify actions for each clone instance
 422  *     -> "post-" notifications complete pseudo-action for clone
 423  *
 424  * \param[in,out] rsc       Clone that notifications are for
 425  * \param[in]     task      Name of action that notifications are for
 426  * \param[in,out] action    If not NULL, create a "pre-" pseudo-action ordered
 427  *                          before a "pre-" complete pseudo-action, ordered
 428  *                          before this action
 429  * \param[in,out] complete  If not NULL, create a "post-" pseudo-action ordered
 430  *                          after this action, and a "post-" complete
 431  *                          pseudo-action ordered after that
 432  *
 433  * \return Newly created notification data
 434  */
 435 notify_data_t *
 436 pe__action_notif_pseudo_ops(pcmk_resource_t *rsc, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 437                             pcmk_action_t *action, pcmk_action_t *complete)
 438 {
 439     notify_data_t *n_data = NULL;
 440 
 441     if (!pcmk_is_set(rsc->flags, pcmk__rsc_notify)) {
 442         return NULL;
 443     }
 444 
 445     n_data = pcmk__assert_alloc(1, sizeof(notify_data_t));
 446 
 447     n_data->action = task;
 448 
 449     if (action != NULL) { // Need "pre-" pseudo-actions
 450 
 451         // Create "pre-" notify pseudo-action for clone
 452         n_data->pre = new_notify_pseudo_action(rsc, action, PCMK_ACTION_NOTIFY,
 453                                                "pre");
 454         pcmk__set_action_flags(n_data->pre, pcmk__action_runnable);
 455         pcmk__insert_meta(n_data->pre, "notify_type", "pre");
 456         pcmk__insert_meta(n_data->pre, "notify_operation", n_data->action);
 457 
 458         // Create "pre-" notifications complete pseudo-action for clone
 459         n_data->pre_done = new_notify_pseudo_action(rsc, action,
 460                                                     PCMK_ACTION_NOTIFIED,
 461                                                     "confirmed-pre");
 462         pcmk__set_action_flags(n_data->pre_done, pcmk__action_runnable);
 463         pcmk__insert_meta(n_data->pre_done, "notify_type", "pre");
 464         pcmk__insert_meta(n_data->pre_done, "notify_operation", n_data->action);
 465 
 466         // Order "pre-" -> "pre-" complete -> original action
 467         order_actions(n_data->pre, n_data->pre_done, pcmk__ar_ordered);
 468         order_actions(n_data->pre_done, action, pcmk__ar_ordered);
 469     }
 470 
 471     if (complete != NULL) { // Need "post-" pseudo-actions
 472 
 473         // Create "post-" notify pseudo-action for clone
 474         n_data->post = new_notify_pseudo_action(rsc, complete,
 475                                                 PCMK_ACTION_NOTIFY, "post");
 476         n_data->post->priority = PCMK_SCORE_INFINITY;
 477         if (pcmk_is_set(complete->flags, pcmk__action_runnable)) {
 478             pcmk__set_action_flags(n_data->post, pcmk__action_runnable);
 479         } else {
 480             pcmk__clear_action_flags(n_data->post, pcmk__action_runnable);
 481         }
 482         pcmk__insert_meta(n_data->post, "notify_type", "post");
 483         pcmk__insert_meta(n_data->post, "notify_operation", n_data->action);
 484 
 485         // Create "post-" notifications complete pseudo-action for clone
 486         n_data->post_done = new_notify_pseudo_action(rsc, complete,
 487                                                      PCMK_ACTION_NOTIFIED,
 488                                                      "confirmed-post");
 489         n_data->post_done->priority = PCMK_SCORE_INFINITY;
 490         if (pcmk_is_set(complete->flags, pcmk__action_runnable)) {
 491             pcmk__set_action_flags(n_data->post_done, pcmk__action_runnable);
 492         } else {
 493             pcmk__clear_action_flags(n_data->post_done, pcmk__action_runnable);
 494         }
 495         pcmk__insert_meta(n_data->post_done, "notify_type", "post");
 496         pcmk__insert_meta(n_data->post_done,
 497                           "notify_operation", n_data->action);
 498 
 499         // Order original action complete -> "post-" -> "post-" complete
 500         order_actions(complete, n_data->post, pcmk__ar_first_implies_then);
 501         order_actions(n_data->post, n_data->post_done,
 502                       pcmk__ar_first_implies_then);
 503     }
 504 
 505     // If we created both, order "pre-" complete -> "post-"
 506     if ((action != NULL) && (complete != NULL)) {
 507         order_actions(n_data->pre_done, n_data->post, pcmk__ar_ordered);
 508     }
 509     return n_data;
 510 }
 511 
 512 /*!
 513  * \internal
 514  * \brief Create a new notification entry
 515  *
 516  * \param[in] rsc   Resource for notification
 517  * \param[in] node  Node for notification
 518  *
 519  * \return Newly allocated notification entry
 520  * \note The caller is responsible for freeing the return value.
 521  */
 522 static notify_entry_t *
 523 new_notify_entry(const pcmk_resource_t *rsc, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 524 {
 525     notify_entry_t *entry = pcmk__assert_alloc(1, sizeof(notify_entry_t));
 526 
 527     entry->rsc = rsc;
 528     entry->node = node;
 529     return entry;
 530 }
 531 
 532 /*!
 533  * \internal
 534  * \brief Add notification data for resource state and optionally actions
 535  *
 536  * \param[in,out] rsc       Clone or clone instance being notified
 537  * \param[in]     activity  Whether to add notification entries for actions
 538  * \param[in,out] n_data    Notification data for clone
 539  */
 540 static void
 541 collect_resource_data(pcmk_resource_t *rsc, bool activity,
     /* [previous][next][first][last][top][bottom][index][help] */
 542                       notify_data_t *n_data)
 543 {
 544     const GList *iter = NULL;
 545     notify_entry_t *entry = NULL;
 546     const pcmk_node_t *node = NULL;
 547 
 548     if (n_data == NULL) {
 549         return;
 550     }
 551 
 552     if (n_data->allowed_nodes == NULL) {
 553         n_data->allowed_nodes = rsc->priv->allowed_nodes;
 554     }
 555 
 556     // If this is a clone, call recursively for each instance
 557     if (rsc->priv->children != NULL) {
 558         for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
 559             pcmk_resource_t *child = iter->data;
 560 
 561             collect_resource_data(child, activity, n_data);
 562         }
 563         return;
 564     }
 565 
 566     // This is a notification for a single clone instance
 567 
 568     if (rsc->priv->active_nodes != NULL) {
 569         node = rsc->priv->active_nodes->data; // First is sufficient
 570     }
 571     entry = new_notify_entry(rsc, node);
 572 
 573     // Add notification indicating the resource state
 574     switch (rsc->priv->orig_role) {
 575         case pcmk_role_stopped:
 576             n_data->inactive = g_list_prepend(n_data->inactive, entry);
 577             break;
 578 
 579         case pcmk_role_started:
 580             n_data->active = g_list_prepend(n_data->active, entry);
 581             break;
 582 
 583         case pcmk_role_unpromoted:
 584             n_data->unpromoted = g_list_prepend(n_data->unpromoted, entry);
 585             n_data->active = g_list_prepend(n_data->active,
 586                                             dup_notify_entry(entry));
 587             break;
 588 
 589         case pcmk_role_promoted:
 590             n_data->promoted = g_list_prepend(n_data->promoted, entry);
 591             n_data->active = g_list_prepend(n_data->active,
 592                                             dup_notify_entry(entry));
 593             break;
 594 
 595         default:
 596             pcmk__sched_err(rsc->priv->scheduler,
 597                             "Resource %s role on %s (%s) is not supported for "
 598                             "notifications (bug?)",
 599                             rsc->id, pcmk__node_name(node),
 600                             pcmk_role_text(rsc->priv->orig_role));
 601             free(entry);
 602             break;
 603     }
 604 
 605     if (!activity) {
 606         return;
 607     }
 608 
 609     // Add notification entries for each of the resource's actions
 610     for (iter = rsc->priv->actions; iter != NULL; iter = iter->next) {
 611         const pcmk_action_t *op = (const pcmk_action_t *) iter->data;
 612 
 613         if (!pcmk_is_set(op->flags, pcmk__action_optional)
 614             && (op->node != NULL)) {
 615             enum pcmk__action_type task = pcmk__parse_action(op->task);
 616 
 617             if ((task == pcmk__action_stop) && op->node->details->unclean) {
 618                 // Create anyway (additional noise if node can't be fenced)
 619             } else if (!pcmk_is_set(op->flags, pcmk__action_runnable)) {
 620                 continue;
 621             }
 622 
 623             entry = new_notify_entry(rsc, op->node);
 624 
 625             switch (task) {
 626                 case pcmk__action_start:
 627                     n_data->start = g_list_prepend(n_data->start, entry);
 628                     break;
 629                 case pcmk__action_stop:
 630                     n_data->stop = g_list_prepend(n_data->stop, entry);
 631                     break;
 632                 case pcmk__action_promote:
 633                     n_data->promote = g_list_prepend(n_data->promote, entry);
 634                     break;
 635                 case pcmk__action_demote:
 636                     n_data->demote = g_list_prepend(n_data->demote, entry);
 637                     break;
 638                 default:
 639                     free(entry);
 640                     break;
 641             }
 642         }
 643     }
 644 }
 645 
 646 // For (char *) value
 647 #define add_notify_env(n_data, key, value) do {                         \
 648          n_data->keys = pcmk_prepend_nvpair(n_data->keys, key, value);  \
 649     } while (0)
 650 
 651 // For (GString *) value
 652 #define add_notify_env_gs(n_data, key, value) do {                      \
 653          n_data->keys = pcmk_prepend_nvpair(n_data->keys, key,          \
 654                                             (const char *) value->str); \
 655     } while (0)
 656 
 657 // For (GString *) value
 658 #define add_notify_env_free_gs(n_data, key, value) do {                 \
 659          n_data->keys = pcmk_prepend_nvpair(n_data->keys, key,          \
 660                                             (const char *) value->str); \
 661          g_string_free(value, TRUE); value = NULL;                      \
 662     } while (0)
 663 
 664 /*!
 665  * \internal
 666  * \brief Create notification name/value pairs from structured data
 667  *
 668  * \param[in]     rsc       Resource that notification is for
 669  * \param[in,out] n_data    Notification data
 670  */
 671 static void
 672 add_notif_keys(const pcmk_resource_t *rsc, notify_data_t *n_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 673 {
 674     bool required = false; // Whether to make notify actions required
 675     GString *rsc_list = NULL;
 676     GString *node_list = NULL;
 677     GString *metal_list = NULL;
 678     const char *source = NULL;
 679     GList *nodes = NULL;
 680 
 681     n_data->stop = notify_entries_to_strings(n_data->stop,
 682                                              &rsc_list, &node_list);
 683     if ((strcmp(" ", (const char *) rsc_list->str) != 0)
 684         && pcmk__str_eq(n_data->action, PCMK_ACTION_STOP, pcmk__str_none)) {
 685         required = true;
 686     }
 687     add_notify_env_free_gs(n_data, "notify_stop_resource", rsc_list);
 688     add_notify_env_free_gs(n_data, "notify_stop_uname", node_list);
 689 
 690     if ((n_data->start != NULL)
 691         && pcmk__str_eq(n_data->action, PCMK_ACTION_START, pcmk__str_none)) {
 692         required = true;
 693     }
 694     n_data->start = notify_entries_to_strings(n_data->start,
 695                                               &rsc_list, &node_list);
 696     add_notify_env_free_gs(n_data, "notify_start_resource", rsc_list);
 697     add_notify_env_free_gs(n_data, "notify_start_uname", node_list);
 698 
 699     if ((n_data->demote != NULL)
 700         && pcmk__str_eq(n_data->action, PCMK_ACTION_DEMOTE, pcmk__str_none)) {
 701         required = true;
 702     }
 703     n_data->demote = notify_entries_to_strings(n_data->demote,
 704                                                &rsc_list, &node_list);
 705     add_notify_env_free_gs(n_data, "notify_demote_resource", rsc_list);
 706     add_notify_env_free_gs(n_data, "notify_demote_uname", node_list);
 707 
 708     if ((n_data->promote != NULL)
 709         && pcmk__str_eq(n_data->action, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
 710         required = true;
 711     }
 712     n_data->promote = notify_entries_to_strings(n_data->promote,
 713                                                 &rsc_list, &node_list);
 714     add_notify_env_free_gs(n_data, "notify_promote_resource", rsc_list);
 715     add_notify_env_free_gs(n_data, "notify_promote_uname", node_list);
 716 
 717     n_data->active = notify_entries_to_strings(n_data->active,
 718                                                &rsc_list, &node_list);
 719     add_notify_env_free_gs(n_data, "notify_active_resource", rsc_list);
 720     add_notify_env_free_gs(n_data, "notify_active_uname", node_list);
 721 
 722     n_data->unpromoted = notify_entries_to_strings(n_data->unpromoted,
 723                                                    &rsc_list, &node_list);
 724     add_notify_env_gs(n_data, "notify_unpromoted_resource", rsc_list);
 725     add_notify_env_gs(n_data, "notify_unpromoted_uname", node_list);
 726 
 727     // Deprecated: kept for backward compatibility with older resource agents
 728     add_notify_env_free_gs(n_data, "notify_slave_resource", rsc_list);
 729     add_notify_env_free_gs(n_data, "notify_slave_uname", node_list);
 730 
 731     n_data->promoted = notify_entries_to_strings(n_data->promoted,
 732                                                  &rsc_list, &node_list);
 733     add_notify_env_gs(n_data, "notify_promoted_resource", rsc_list);
 734     add_notify_env_gs(n_data, "notify_promoted_uname", node_list);
 735 
 736     // Deprecated: kept for backward compatibility with older resource agents
 737     add_notify_env_free_gs(n_data, "notify_master_resource", rsc_list);
 738     add_notify_env_free_gs(n_data, "notify_master_uname", node_list);
 739 
 740     n_data->inactive = notify_entries_to_strings(n_data->inactive,
 741                                                  &rsc_list, NULL);
 742     add_notify_env_free_gs(n_data, "notify_inactive_resource", rsc_list);
 743 
 744     nodes = g_hash_table_get_values(n_data->allowed_nodes);
 745     if (!pcmk__is_daemon) {
 746         /* For display purposes, sort the node list, for consistent
 747          * regression test output (while avoiding the performance hit
 748          * for the live cluster).
 749          */
 750         nodes = g_list_sort(nodes, pe__cmp_node_name);
 751     }
 752     get_node_names(nodes, &node_list, NULL);
 753     add_notify_env_free_gs(n_data, "notify_available_uname", node_list);
 754     g_list_free(nodes);
 755 
 756     source = g_hash_table_lookup(rsc->priv->meta,
 757                                  PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
 758     if (pcmk__str_eq(PCMK_VALUE_HOST, source, pcmk__str_none)) {
 759         get_node_names(rsc->priv->scheduler->nodes, &node_list, &metal_list);
 760         add_notify_env_free_gs(n_data, "notify_all_hosts", metal_list);
 761     } else {
 762         get_node_names(rsc->priv->scheduler->nodes, &node_list, NULL);
 763     }
 764     add_notify_env_free_gs(n_data, "notify_all_uname", node_list);
 765 
 766     if (required && (n_data->pre != NULL)) {
 767         pcmk__clear_action_flags(n_data->pre, pcmk__action_optional);
 768         pcmk__clear_action_flags(n_data->pre_done, pcmk__action_optional);
 769     }
 770 
 771     if (required && (n_data->post != NULL)) {
 772         pcmk__clear_action_flags(n_data->post, pcmk__action_optional);
 773         pcmk__clear_action_flags(n_data->post_done, pcmk__action_optional);
 774     }
 775 }
 776 
 777 /*
 778  * \internal
 779  * \brief Find any remote connection start relevant to an action
 780  *
 781  * \param[in] action  Action to check
 782  *
 783  * \return If action is behind a remote connection, connection's start
 784  */
 785 static pcmk_action_t *
 786 find_remote_start(pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 787 {
 788     if ((action != NULL) && (action->node != NULL)) {
 789         pcmk_resource_t *remote_rsc = action->node->priv->remote;
 790 
 791         if (remote_rsc != NULL) {
 792             return find_first_action(remote_rsc->priv->actions, NULL,
 793                                      PCMK_ACTION_START,
 794                                      NULL);
 795         }
 796     }
 797     return NULL;
 798 }
 799 
 800 /*!
 801  * \internal
 802  * \brief Create notify actions, and add notify data to original actions
 803  *
 804  * \param[in,out] rsc     Clone or clone instance that notification is for
 805  * \param[in,out] n_data  Clone notification data for some action
 806  */
 807 static void
 808 create_notify_actions(pcmk_resource_t *rsc, notify_data_t *n_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 809 {
 810     GList *iter = NULL;
 811     pcmk_action_t *stop = NULL;
 812     pcmk_action_t *start = NULL;
 813     enum pcmk__action_type task = pcmk__parse_action(n_data->action);
 814 
 815     // If this is a clone, call recursively for each instance
 816     if (rsc->priv->children != NULL) {
 817         g_list_foreach(rsc->priv->children, (GFunc) create_notify_actions,
 818                        n_data);
 819         return;
 820     }
 821 
 822     // Add notification meta-attributes to original actions
 823     for (iter = rsc->priv->actions; iter != NULL; iter = iter->next) {
 824         pcmk_action_t *op = (pcmk_action_t *) iter->data;
 825 
 826         if (!pcmk_is_set(op->flags, pcmk__action_optional)
 827             && (op->node != NULL)) {
 828             switch (pcmk__parse_action(op->task)) {
 829                 case pcmk__action_start:
 830                 case pcmk__action_stop:
 831                 case pcmk__action_promote:
 832                 case pcmk__action_demote:
 833                     add_notify_data_to_action_meta(n_data, op);
 834                     break;
 835                 default:
 836                     break;
 837             }
 838         }
 839     }
 840 
 841     // Skip notify action itself if original action was not needed
 842     switch (task) {
 843         case pcmk__action_start:
 844             if (n_data->start == NULL) {
 845                 pcmk__rsc_trace(rsc, "No notify action needed for %s %s",
 846                                 rsc->id, n_data->action);
 847                 return;
 848             }
 849             break;
 850 
 851         case pcmk__action_promote:
 852             if (n_data->promote == NULL) {
 853                 pcmk__rsc_trace(rsc, "No notify action needed for %s %s",
 854                                 rsc->id, n_data->action);
 855                 return;
 856             }
 857             break;
 858 
 859         case pcmk__action_demote:
 860             if (n_data->demote == NULL) {
 861                 pcmk__rsc_trace(rsc, "No notify action needed for %s %s",
 862                                 rsc->id, n_data->action);
 863                 return;
 864             }
 865             break;
 866 
 867         default:
 868             // We cannot do same for stop because it might be implied by fencing
 869             break;
 870     }
 871 
 872     pcmk__rsc_trace(rsc, "Creating notify actions for %s %s",
 873                     rsc->id, n_data->action);
 874 
 875     // Create notify actions for stop or demote
 876     if ((rsc->priv->orig_role != pcmk_role_stopped)
 877         && ((task == pcmk__action_stop) || (task == pcmk__action_demote))) {
 878 
 879         stop = find_first_action(rsc->priv->actions, NULL, PCMK_ACTION_STOP,
 880                                  NULL);
 881 
 882         for (iter = rsc->priv->active_nodes;
 883              iter != NULL; iter = iter->next) {
 884 
 885             pcmk_node_t *current_node = (pcmk_node_t *) iter->data;
 886 
 887             /* If a stop is a pseudo-action implied by fencing, don't try to
 888              * notify the node getting fenced.
 889              */
 890             if ((stop != NULL)
 891                 && pcmk_is_set(stop->flags, pcmk__action_pseudo)
 892                 && (current_node->details->unclean
 893                     || pcmk_is_set(current_node->priv->flags,
 894                                    pcmk__node_remote_reset))) {
 895                 continue;
 896             }
 897 
 898             new_notify_action(rsc, current_node, n_data->pre,
 899                               n_data->pre_done, n_data);
 900 
 901             if ((task == pcmk__action_demote) || (stop == NULL)
 902                 || pcmk_is_set(stop->flags, pcmk__action_optional)) {
 903                 new_post_notify_action(rsc, current_node, n_data);
 904             }
 905         }
 906     }
 907 
 908     // Create notify actions for start or promote
 909     if ((rsc->priv->next_role != pcmk_role_stopped)
 910         && ((task == pcmk__action_start) || (task == pcmk__action_promote))) {
 911 
 912         start = find_first_action(rsc->priv->actions, NULL,
 913                                   PCMK_ACTION_START, NULL);
 914         if (start != NULL) {
 915             pcmk_action_t *remote_start = find_remote_start(start);
 916 
 917             if ((remote_start != NULL)
 918                 && !pcmk_is_set(remote_start->flags, pcmk__action_runnable)) {
 919                 /* Start and promote actions for a clone instance behind
 920                  * a Pacemaker Remote connection happen after the
 921                  * connection starts. If the connection start is blocked, do
 922                  * not schedule notifications for these actions.
 923                  */
 924                 return;
 925             }
 926         }
 927         if (rsc->priv->assigned_node == NULL) {
 928             pcmk__sched_err(rsc->priv->scheduler,
 929                             "Next role '%s' but %s is not allocated",
 930                             pcmk_role_text(rsc->priv->next_role), rsc->id);
 931             return;
 932         }
 933         if ((task != pcmk__action_start) || (start == NULL)
 934             || pcmk_is_set(start->flags, pcmk__action_optional)) {
 935 
 936             new_notify_action(rsc, rsc->priv->assigned_node, n_data->pre,
 937                               n_data->pre_done, n_data);
 938         }
 939         new_post_notify_action(rsc, rsc->priv->assigned_node, n_data);
 940     }
 941 }
 942 
 943 /*!
 944  * \internal
 945  * \brief Create notification data and actions for one clone action
 946  *
 947  * \param[in,out] rsc     Clone resource that notification is for
 948  * \param[in,out] n_data  Clone notification data for some action
 949  */
 950 void
 951 pe__create_action_notifications(pcmk_resource_t *rsc, notify_data_t *n_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 952 {
 953     if ((rsc == NULL) || (n_data == NULL)) {
 954         return;
 955     }
 956     collect_resource_data(rsc, true, n_data);
 957     add_notif_keys(rsc, n_data);
 958     create_notify_actions(rsc, n_data);
 959 }
 960 
 961 /*!
 962  * \internal
 963  * \brief Free notification data for one action
 964  *
 965  * \param[in,out] n_data  Notification data to free
 966  */
 967 void
 968 pe__free_action_notification_data(notify_data_t *n_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 969 {
 970     if (n_data == NULL) {
 971         return;
 972     }
 973     g_list_free_full(n_data->stop, free);
 974     g_list_free_full(n_data->start, free);
 975     g_list_free_full(n_data->demote, free);
 976     g_list_free_full(n_data->promote, free);
 977     g_list_free_full(n_data->promoted, free);
 978     g_list_free_full(n_data->unpromoted, free);
 979     g_list_free_full(n_data->active, free);
 980     g_list_free_full(n_data->inactive, free);
 981     pcmk_free_nvpairs(n_data->keys);
 982     free(n_data);
 983 }
 984 
 985 /*!
 986  * \internal
 987  * \brief Order clone "notifications complete" pseudo-action after fencing
 988  *
 989  * If a stop action is implied by fencing, the usual notification pseudo-actions
 990  * will not be sufficient to order things properly, or even create all needed
 991  * notifications if the clone is also stopping on another node, and another
 992  * clone is ordered after it. This function creates new notification
 993  * pseudo-actions relative to the fencing to ensure everything works properly.
 994  *
 995  * \param[in]     stop        Stop action implied by fencing
 996  * \param[in,out] rsc         Clone resource that notification is for
 997  * \param[in,out] stonith_op  Fencing action that implies \p stop
 998  */
 999 void
1000 pe__order_notifs_after_fencing(const pcmk_action_t *stop, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1001                                pcmk_action_t *stonith_op)
1002 {
1003     notify_data_t *n_data;
1004 
1005     crm_info("Ordering notifications for implied %s after fencing", stop->uuid);
1006     n_data = pe__action_notif_pseudo_ops(rsc, PCMK_ACTION_STOP, NULL,
1007                                          stonith_op);
1008 
1009     if (n_data != NULL) {
1010         collect_resource_data(rsc, false, n_data);
1011         add_notify_env(n_data, "notify_stop_resource", rsc->id);
1012         add_notify_env(n_data, "notify_stop_uname", stop->node->priv->name);
1013         create_notify_actions(uber_parent(rsc), n_data);
1014         pe__free_action_notification_data(n_data);
1015     }
1016 }

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