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

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