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

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