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

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