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

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