root/lib/pacemaker/pcmk_sched_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. pcmk__clone_notif_pseudo_ops
  11. new_notify_entry
  12. collect_resource_data
  13. add_notif_keys
  14. find_remote_start
  15. create_notify_actions
  16. pcmk__create_notifications
  17. pcmk__free_notification_data
  18. pcmk__order_notifs_after_fencing

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

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