root/lib/pacemaker/pcmk_sched_notif.c

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

DEFINITIONS

This source file includes following definitions.
  1. sort_notify_entries
  2. dup_notify_entry
  3. expand_node_list
  4. expand_list
  5. dup_attr
  6. add_notify_data_to_action_meta
  7. pe_notify
  8. pe_post_notify
  9. create_notification_boundaries
  10. collect_notification_data
  11. pcmk__create_notification_keys
  12. find_remote_start
  13. create_notifications
  14. free_notification_data
  15. create_secondary_notification

   1 /*
   2  * Copyright 2004-2021 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 extern bool pcmk__is_daemon;
  15 
  16 typedef struct notify_entry_s {
  17     pe_resource_t *rsc;
  18     pe_node_t *node;
  19 } notify_entry_t;
  20 
  21 static gint
  22 sort_notify_entries(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
  23 {
  24     int tmp;
  25     const notify_entry_t *entry_a = a;
  26     const notify_entry_t *entry_b = b;
  27 
  28     if (entry_a == NULL && entry_b == NULL) {
  29         return 0;
  30     }
  31     if (entry_a == NULL) {
  32         return 1;
  33     }
  34     if (entry_b == NULL) {
  35         return -1;
  36     }
  37 
  38     if (entry_a->rsc == NULL && entry_b->rsc == NULL) {
  39         return 0;
  40     }
  41     if (entry_a->rsc == NULL) {
  42         return 1;
  43     }
  44     if (entry_b->rsc == NULL) {
  45         return -1;
  46     }
  47 
  48     tmp = strcmp(entry_a->rsc->id, entry_b->rsc->id);
  49     if (tmp != 0) {
  50         return tmp;
  51     }
  52 
  53     if (entry_a->node == NULL && entry_b->node == NULL) {
  54         return 0;
  55     }
  56     if (entry_a->node == NULL) {
  57         return 1;
  58     }
  59     if (entry_b->node == NULL) {
  60         return -1;
  61     }
  62 
  63     return strcmp(entry_a->node->details->id, entry_b->node->details->id);
  64 }
  65 
  66 static notify_entry_t *dup_notify_entry(notify_entry_t *entry)
     /* [previous][next][first][last][top][bottom][index][help] */
  67 {
  68     notify_entry_t *dup = malloc(sizeof(notify_entry_t));
  69 
  70     CRM_ASSERT(dup != NULL);
  71     dup->rsc = entry->rsc;
  72     dup->node = entry->node;
  73     return dup;
  74 }
  75 
  76 static void
  77 expand_node_list(GList *list, char **uname, char **metal)
     /* [previous][next][first][last][top][bottom][index][help] */
  78 {
  79     GList *gIter = NULL;
  80     char *node_list = NULL;
  81     char *metal_list = NULL;
  82     size_t node_list_len = 0;
  83     size_t metal_list_len = 0;
  84 
  85     CRM_ASSERT(uname != NULL);
  86     if (list == NULL) {
  87         *uname = strdup(" ");
  88         if(metal) {
  89             *metal = strdup(" ");
  90         }
  91         return;
  92     }
  93 
  94     for (gIter = list; gIter != NULL; gIter = gIter->next) {
  95         pe_node_t *node = (pe_node_t *) gIter->data;
  96 
  97         if (node->details->uname == NULL) {
  98             continue;
  99         }
 100         pcmk__add_word(&node_list, &node_list_len, node->details->uname);
 101         if(metal) {
 102             if(node->details->remote_rsc
 103                && node->details->remote_rsc->container
 104                && node->details->remote_rsc->container->running_on) {
 105                 node = pe__current_node(node->details->remote_rsc->container);
 106             }
 107 
 108             if (node->details->uname == NULL) {
 109                 continue;
 110             }
 111             pcmk__add_word(&metal_list, &metal_list_len, node->details->uname);
 112         }
 113     }
 114 
 115     *uname = node_list;
 116     if(metal) {
 117         *metal = metal_list;
 118     }
 119 }
 120 
 121 /*!
 122  * \internal
 123  * \brief Separate a list of notification entries into resource and node strings
 124  *
 125  * \param[in,out] list       List of notify_entry_t* (will be sorted here)
 126  * \param[out]    rsc_list   String list of clone instances from \p list
 127  * \param[out]    node_list  String list of nodes from \p list
 128  *
 129  * \return (Possibly new head of) sorted \p list
 130  */
 131 static GList *
 132 expand_list(GList *list, char **rsc_list, char **node_list)
     /* [previous][next][first][last][top][bottom][index][help] */
 133 {
 134     const char *last_rsc_id = NULL;
 135     size_t rsc_list_len = 0;
 136     size_t node_list_len = 0;
 137 
 138     CRM_CHECK(rsc_list != NULL, return list);
 139 
 140     // If there are no entries, return "empty" lists
 141     if (list == NULL) {
 142         *rsc_list = strdup(" ");
 143         if (node_list) {
 144             *node_list = strdup(" ");
 145         }
 146         return list;
 147     }
 148 
 149     // Initialize output lists to NULL
 150     *rsc_list = NULL;
 151     if (node_list) {
 152         *node_list = NULL;
 153     }
 154 
 155     // Sort input list for user-friendliness (and ease of filtering duplicates)
 156     list = g_list_sort(list, sort_notify_entries);
 157 
 158     for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
 159         notify_entry_t *entry = (notify_entry_t *) gIter->data;
 160 
 161         // Entry must have a resource (with ID)
 162         CRM_LOG_ASSERT((entry != NULL) && (entry->rsc != NULL)
 163                        && (entry->rsc->id != NULL));
 164         if ((entry == NULL) || (entry->rsc == NULL)
 165             || (entry->rsc->id == NULL)) {
 166             continue;
 167         }
 168 
 169         // Entry must have a node unless listing inactive resources
 170         CRM_LOG_ASSERT((node_list == NULL) || (entry->node != NULL));
 171         if ((node_list != NULL) && (entry->node == NULL)) {
 172             continue;
 173         }
 174 
 175         // Don't add duplicates of a particular clone instance
 176         if (pcmk__str_eq(entry->rsc->id, last_rsc_id, pcmk__str_none)) {
 177             continue;
 178         }
 179         last_rsc_id = entry->rsc->id;
 180         pcmk__add_word(rsc_list, &rsc_list_len, entry->rsc->id);
 181         if ((node_list != NULL) && (entry->node->details->uname != NULL)) {
 182             pcmk__add_word(node_list, &node_list_len,
 183                            entry->node->details->uname);
 184         }
 185     }
 186     return list;
 187 }
 188 
 189 static void
 190 dup_attr(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 191 {
 192     add_hash_param(user_data, key, value);
 193 }
 194 
 195 static void
 196 add_notify_data_to_action_meta(notify_data_t *n_data, pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 197 {
 198     for (GSList *item = n_data->keys; item; item = item->next) {
 199         pcmk_nvpair_t *nvpair = item->data;
 200 
 201         add_hash_param(action->meta, nvpair->name, nvpair->value);
 202     }
 203 }
 204 
 205 static pe_action_t *
 206 pe_notify(pe_resource_t * rsc, pe_node_t * node, pe_action_t * op, pe_action_t * confirm,
     /* [previous][next][first][last][top][bottom][index][help] */
 207           notify_data_t * n_data, pe_working_set_t * data_set)
 208 {
 209     char *key = NULL;
 210     pe_action_t *trigger = NULL;
 211     const char *value = NULL;
 212     const char *task = NULL;
 213 
 214     if (op == NULL || confirm == NULL) {
 215         pe_rsc_trace(rsc, "Op=%p confirm=%p", op, confirm);
 216         return NULL;
 217     }
 218 
 219     CRM_CHECK(rsc != NULL, return NULL);
 220     CRM_CHECK(node != NULL, return NULL);
 221 
 222     if (node->details->online == FALSE) {
 223         pe_rsc_trace(rsc, "Skipping notification for %s: node offline", rsc->id);
 224         return NULL;
 225     } else if (!pcmk_is_set(op->flags, pe_action_runnable)) {
 226         pe_rsc_trace(rsc, "Skipping notification for %s: not runnable", op->uuid);
 227         return NULL;
 228     }
 229 
 230     value = g_hash_table_lookup(op->meta, "notify_type");
 231     task = g_hash_table_lookup(op->meta, "notify_operation");
 232 
 233     pe_rsc_trace(rsc, "Creating notify actions for %s: %s (%s-%s)", op->uuid, rsc->id, value, task);
 234 
 235     key = pcmk__notify_key(rsc->id, value, task);
 236     trigger = custom_action(rsc, key, op->task, node,
 237                             pcmk_is_set(op->flags, pe_action_optional),
 238                             TRUE, data_set);
 239     g_hash_table_foreach(op->meta, dup_attr, trigger->meta);
 240     add_notify_data_to_action_meta(n_data, trigger);
 241 
 242     /* pseudo_notify before notify */
 243     pe_rsc_trace(rsc, "Ordering %s before %s (%d->%d)", op->uuid, trigger->uuid, trigger->id,
 244                  op->id);
 245 
 246     order_actions(op, trigger, pe_order_optional);
 247     order_actions(trigger, confirm, pe_order_optional);
 248     return trigger;
 249 }
 250 
 251 static void
 252 pe_post_notify(pe_resource_t * rsc, pe_node_t * node, notify_data_t * n_data, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254     pe_action_t *notify = NULL;
 255 
 256     CRM_CHECK(rsc != NULL, return);
 257 
 258     if (n_data->post == NULL) {
 259         return;                 /* Nothing to do */
 260     }
 261 
 262     notify = pe_notify(rsc, node, n_data->post, n_data->post_done, n_data, data_set);
 263 
 264     if (notify != NULL) {
 265         notify->priority = INFINITY;
 266     }
 267 
 268     if (n_data->post_done) {
 269         GList *gIter = rsc->actions;
 270 
 271         for (; gIter != NULL; gIter = gIter->next) {
 272             pe_action_t *mon = (pe_action_t *) gIter->data;
 273             const char *interval_ms_s = g_hash_table_lookup(mon->meta,
 274                                                             XML_LRM_ATTR_INTERVAL_MS);
 275 
 276             if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
 277                 pe_rsc_trace(rsc, "Skipping %s: interval", mon->uuid);
 278                 continue;
 279             } else if (pcmk__str_eq(mon->task, RSC_CANCEL, pcmk__str_casei)) {
 280                 pe_rsc_trace(rsc, "Skipping %s: cancel", mon->uuid);
 281                 continue;
 282             }
 283 
 284             order_actions(n_data->post_done, mon, pe_order_optional);
 285         }
 286     }
 287 }
 288 
 289 notify_data_t *
 290 create_notification_boundaries(pe_resource_t * rsc, const char *action, pe_action_t * start,
     /* [previous][next][first][last][top][bottom][index][help] */
 291                                pe_action_t * end, pe_working_set_t * data_set)
 292 {
 293     /* Create the pseudo ops that precede and follow the actual notifications */
 294 
 295     /*
 296      * Creates two sequences (conditional on start and end being supplied):
 297      *   pre_notify -> pre_notify_complete -> start, and
 298      *   end -> post_notify -> post_notify_complete
 299      *
 300      * 'start' and 'end' may be the same event or ${X} and ${X}ed as per clones
 301      */
 302     char *key = NULL;
 303     notify_data_t *n_data = NULL;
 304 
 305     if (!pcmk_is_set(rsc->flags, pe_rsc_notify)) {
 306         return NULL;
 307     }
 308 
 309     n_data = calloc(1, sizeof(notify_data_t));
 310     n_data->action = action;
 311 
 312     if (start) {
 313         /* create pre-event notification wrappers */
 314         key = pcmk__notify_key(rsc->id, "pre", start->task);
 315         n_data->pre =
 316             custom_action(rsc, key, RSC_NOTIFY, NULL,
 317                           pcmk_is_set(start->flags, pe_action_optional),
 318                           TRUE, data_set);
 319 
 320         update_action_flags(n_data->pre, pe_action_pseudo, __func__, __LINE__);
 321         update_action_flags(n_data->pre, pe_action_runnable, __func__,
 322                             __LINE__);
 323 
 324         add_hash_param(n_data->pre->meta, "notify_type", "pre");
 325         add_hash_param(n_data->pre->meta, "notify_operation", n_data->action);
 326 
 327         add_hash_param(n_data->pre->meta, "notify_key_type", "pre");
 328         add_hash_param(n_data->pre->meta, "notify_key_operation", start->task);
 329 
 330         /* create pre_notify_complete */
 331         key = pcmk__notify_key(rsc->id, "confirmed-pre", start->task);
 332         n_data->pre_done = custom_action(rsc, key, RSC_NOTIFIED, NULL,
 333                                          pcmk_is_set(start->flags, pe_action_optional),
 334                                          TRUE, data_set);
 335 
 336         update_action_flags(n_data->pre_done, pe_action_pseudo, __func__,
 337                             __LINE__);
 338         update_action_flags(n_data->pre_done, pe_action_runnable, __func__,
 339                             __LINE__);
 340 
 341         add_hash_param(n_data->pre_done->meta, "notify_type", "pre");
 342         add_hash_param(n_data->pre_done->meta, "notify_operation", n_data->action);
 343 
 344         add_hash_param(n_data->pre_done->meta, "notify_key_type", "confirmed-pre");
 345         add_hash_param(n_data->pre_done->meta, "notify_key_operation", start->task);
 346 
 347         order_actions(n_data->pre_done, start, pe_order_optional);
 348         order_actions(n_data->pre, n_data->pre_done, pe_order_optional);
 349     }
 350 
 351     if (end) {
 352         /* create post-event notification wrappers */
 353         key = pcmk__notify_key(rsc->id, "post", end->task);
 354         n_data->post = custom_action(rsc, key, RSC_NOTIFY, NULL,
 355                                      pcmk_is_set(end->flags, pe_action_optional),
 356                                      TRUE, data_set);
 357 
 358         n_data->post->priority = INFINITY;
 359         update_action_flags(n_data->post, pe_action_pseudo, __func__,
 360                             __LINE__);
 361         if (pcmk_is_set(end->flags, pe_action_runnable)) {
 362             update_action_flags(n_data->post, pe_action_runnable, __func__,
 363                                 __LINE__);
 364         } else {
 365             update_action_flags(n_data->post, pe_action_runnable | pe_action_clear,
 366                                 __func__, __LINE__);
 367         }
 368 
 369         add_hash_param(n_data->post->meta, "notify_type", "post");
 370         add_hash_param(n_data->post->meta, "notify_operation", n_data->action);
 371 
 372         add_hash_param(n_data->post->meta, "notify_key_type", "post");
 373         add_hash_param(n_data->post->meta, "notify_key_operation", end->task);
 374 
 375         /* create post_notify_complete */
 376         key = pcmk__notify_key(rsc->id, "confirmed-post", end->task);
 377         n_data->post_done = custom_action(rsc, key, RSC_NOTIFIED, NULL,
 378                                           pcmk_is_set(end->flags, pe_action_optional),
 379                                           TRUE, data_set);
 380 
 381         n_data->post_done->priority = INFINITY;
 382         update_action_flags(n_data->post_done, pe_action_pseudo, __func__,
 383                             __LINE__);
 384         if (pcmk_is_set(end->flags, pe_action_runnable)) {
 385             update_action_flags(n_data->post_done, pe_action_runnable,
 386                                 __func__, __LINE__);
 387         } else {
 388             update_action_flags(n_data->post_done, pe_action_runnable | pe_action_clear,
 389                                 __func__, __LINE__);
 390         }
 391 
 392         add_hash_param(n_data->post_done->meta, "notify_type", "post");
 393         add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action);
 394 
 395         add_hash_param(n_data->post_done->meta, "notify_key_type", "confirmed-post");
 396         add_hash_param(n_data->post_done->meta, "notify_key_operation", end->task);
 397 
 398         order_actions(end, n_data->post, pe_order_implies_then);
 399         order_actions(n_data->post, n_data->post_done, pe_order_implies_then);
 400     }
 401 
 402     if (start && end) {
 403         order_actions(n_data->pre_done, n_data->post, pe_order_optional);
 404     }
 405     return n_data;
 406 }
 407 
 408 void
 409 collect_notification_data(pe_resource_t * rsc, gboolean state, gboolean activity,
     /* [previous][next][first][last][top][bottom][index][help] */
 410                           notify_data_t * n_data)
 411 {
 412 
 413     if(n_data->allowed_nodes == NULL) {
 414         n_data->allowed_nodes = rsc->allowed_nodes;
 415     }
 416 
 417     if (rsc->children) {
 418         GList *gIter = rsc->children;
 419 
 420         for (; gIter != NULL; gIter = gIter->next) {
 421             pe_resource_t *child = (pe_resource_t *) gIter->data;
 422 
 423             collect_notification_data(child, state, activity, n_data);
 424         }
 425         return;
 426     }
 427 
 428     if (state) {
 429         notify_entry_t *entry = NULL;
 430 
 431         entry = calloc(1, sizeof(notify_entry_t));
 432         entry->rsc = rsc;
 433         if (rsc->running_on) {
 434             /* we only take the first one */
 435             entry->node = rsc->running_on->data;
 436         }
 437 
 438         pe_rsc_trace(rsc, "%s state: %s", rsc->id, role2text(rsc->role));
 439 
 440         switch (rsc->role) {
 441             case RSC_ROLE_STOPPED:
 442                 n_data->inactive = g_list_prepend(n_data->inactive, entry);
 443                 break;
 444             case RSC_ROLE_STARTED:
 445                 n_data->active = g_list_prepend(n_data->active, entry);
 446                 break;
 447             case RSC_ROLE_UNPROMOTED:
 448                 n_data->unpromoted = g_list_prepend(n_data->unpromoted, entry);
 449                 n_data->active = g_list_prepend(n_data->active,
 450                                                 dup_notify_entry(entry));
 451                 break;
 452             case RSC_ROLE_PROMOTED:
 453                 n_data->promoted = g_list_prepend(n_data->promoted, entry);
 454                 n_data->active = g_list_prepend(n_data->active,
 455                                                 dup_notify_entry(entry));
 456                 break;
 457             default:
 458                 crm_err("Unsupported notify role");
 459                 free(entry);
 460                 break;
 461         }
 462     }
 463 
 464     if (activity) {
 465         notify_entry_t *entry = NULL;
 466         enum action_tasks task;
 467 
 468         GList *gIter = rsc->actions;
 469 
 470         for (; gIter != NULL; gIter = gIter->next) {
 471             pe_action_t *op = (pe_action_t *) gIter->data;
 472 
 473             if (!pcmk_is_set(op->flags, pe_action_optional)
 474                 && (op->node != NULL)) {
 475 
 476                 task = text2task(op->task);
 477 
 478                 if(task == stop_rsc && op->node->details->unclean) {
 479                     // Create anyway (additional noise if node can't be fenced)
 480                 } else if (!pcmk_is_set(op->flags, pe_action_runnable)) {
 481                     continue;
 482                 }
 483 
 484                 entry = calloc(1, sizeof(notify_entry_t));
 485                 entry->node = op->node;
 486                 entry->rsc = rsc;
 487 
 488                 switch (task) {
 489                     case start_rsc:
 490                         n_data->start = g_list_prepend(n_data->start, entry);
 491                         break;
 492                     case stop_rsc:
 493                         n_data->stop = g_list_prepend(n_data->stop, entry);
 494                         break;
 495                     case action_promote:
 496                         n_data->promote = g_list_prepend(n_data->promote, entry);
 497                         break;
 498                     case action_demote:
 499                         n_data->demote = g_list_prepend(n_data->demote, entry);
 500                         break;
 501                     default:
 502                         free(entry);
 503                         break;
 504                 }
 505             }
 506         }
 507     }
 508 }
 509 
 510 #define add_notify_env(n_data, key, value) do {                         \
 511          n_data->keys = pcmk_prepend_nvpair(n_data->keys, key, value);  \
 512     } while (0)
 513 
 514 #define add_notify_env_free(n_data, key, value) do {                    \
 515          n_data->keys = pcmk_prepend_nvpair(n_data->keys, key, value);  \
 516          free(value); value = NULL;                                     \
 517     } while (0)
 518 
 519 /*!
 520  * \internal
 521  * \brief Create notification name/value pairs from raw data
 522  *
 523  * \param[in]     rsc       Resource that notification is for
 524  * \param[in,out] n_data    Notification data
 525  * \param[in]     data_set  Cluster working set
 526  */
 527 void
 528 pcmk__create_notification_keys(pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 529                                notify_data_t *n_data,
 530                                pe_working_set_t *data_set)
 531 {
 532     bool required = false; // Whether to make notify actions required
 533     char *rsc_list = NULL;
 534     char *node_list = NULL;
 535     char *metal_list = NULL;
 536     const char *source = NULL;
 537     GList *nodes = NULL;
 538 
 539     n_data->stop = expand_list(n_data->stop, &rsc_list, &node_list);
 540     if (!pcmk__str_eq(" ", rsc_list, pcmk__str_null_matches)
 541         && pcmk__str_eq(n_data->action, RSC_STOP, pcmk__str_casei)) {
 542         required = true;
 543     }
 544     add_notify_env_free(n_data, "notify_stop_resource", rsc_list);
 545     add_notify_env_free(n_data, "notify_stop_uname", node_list);
 546 
 547     if ((n_data->start != NULL)
 548         && pcmk__str_eq(n_data->action, RSC_START, pcmk__str_casei)) {
 549         required = true;
 550     }
 551     n_data->start = expand_list(n_data->start, &rsc_list, &node_list);
 552     add_notify_env_free(n_data, "notify_start_resource", rsc_list);
 553     add_notify_env_free(n_data, "notify_start_uname", node_list);
 554 
 555     if ((n_data->demote != NULL)
 556         && pcmk__str_eq(n_data->action, RSC_DEMOTE, pcmk__str_casei)) {
 557         required = true;
 558     }
 559     n_data->demote = expand_list(n_data->demote, &rsc_list, &node_list);
 560     add_notify_env_free(n_data, "notify_demote_resource", rsc_list);
 561     add_notify_env_free(n_data, "notify_demote_uname", node_list);
 562 
 563     if ((n_data->promote != NULL)
 564         && pcmk__str_eq(n_data->action, RSC_PROMOTE, pcmk__str_casei)) {
 565         required = true;
 566     }
 567     n_data->promote = expand_list(n_data->promote, &rsc_list, &node_list);
 568     add_notify_env_free(n_data, "notify_promote_resource", rsc_list);
 569     add_notify_env_free(n_data, "notify_promote_uname", node_list);
 570 
 571     n_data->active = expand_list(n_data->active, &rsc_list, &node_list);
 572     add_notify_env_free(n_data, "notify_active_resource", rsc_list);
 573     add_notify_env_free(n_data, "notify_active_uname", node_list);
 574 
 575     n_data->unpromoted = expand_list(n_data->unpromoted, &rsc_list, &node_list);
 576     add_notify_env(n_data, "notify_unpromoted_resource", rsc_list);
 577     add_notify_env(n_data, "notify_unpromoted_uname", node_list);
 578 
 579     // Deprecated: kept for backward compatibility with older resource agents
 580     add_notify_env_free(n_data, "notify_slave_resource", rsc_list);
 581     add_notify_env_free(n_data, "notify_slave_uname", node_list);
 582 
 583     n_data->promoted = expand_list(n_data->promoted, &rsc_list, &node_list);
 584     add_notify_env(n_data, "notify_promoted_resource", rsc_list);
 585     add_notify_env(n_data, "notify_promoted_uname", node_list);
 586 
 587     // Deprecated: kept for backward compatibility with older resource agents
 588     add_notify_env_free(n_data, "notify_master_resource", rsc_list);
 589     add_notify_env_free(n_data, "notify_master_uname", node_list);
 590 
 591     n_data->inactive = expand_list(n_data->inactive, &rsc_list, NULL);
 592     add_notify_env_free(n_data, "notify_inactive_resource", rsc_list);
 593 
 594     nodes = g_hash_table_get_values(n_data->allowed_nodes);
 595     if (!pcmk__is_daemon) {
 596         /* If printing to stdout, sort the node list, for consistent
 597          * regression test output (while avoiding the performance hit
 598          * for the live cluster).
 599          */
 600         nodes = g_list_sort(nodes, sort_node_uname);
 601     }
 602     expand_node_list(nodes, &node_list, NULL);
 603     add_notify_env_free(n_data, "notify_available_uname", node_list);
 604     g_list_free(nodes);
 605 
 606     source = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET);
 607     if (pcmk__str_eq("host", source, pcmk__str_casei)) {
 608         expand_node_list(data_set->nodes, &node_list, &metal_list);
 609         add_notify_env_free(n_data, "notify_all_hosts", metal_list);
 610     } else {
 611         expand_node_list(data_set->nodes, &node_list, NULL);
 612     }
 613     add_notify_env_free(n_data, "notify_all_uname", node_list);
 614 
 615     if (required && n_data->pre) {
 616         update_action_flags(n_data->pre, pe_action_optional | pe_action_clear,
 617                             __func__, __LINE__);
 618         update_action_flags(n_data->pre_done, pe_action_optional | pe_action_clear,
 619                             __func__, __LINE__);
 620     }
 621 
 622     if (required && n_data->post) {
 623         update_action_flags(n_data->post, pe_action_optional | pe_action_clear,
 624                             __func__, __LINE__);
 625         update_action_flags(n_data->post_done, pe_action_optional | pe_action_clear,
 626                             __func__, __LINE__);
 627     }
 628 }
 629 
 630 /*
 631  * \internal
 632  * \brief Find any remote connection start relevant to an action
 633  *
 634  * \param[in] action  Action to chek
 635  *
 636  * \return If action is behind a remote connection, connection's start
 637  */
 638 static pe_action_t *
 639 find_remote_start(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 640 {
 641     if (action && action->node) {
 642         pe_resource_t *remote_rsc = action->node->details->remote_rsc;
 643 
 644         if (remote_rsc) {
 645             return find_first_action(remote_rsc->actions, NULL, RSC_START,
 646                                      NULL);
 647         }
 648     }
 649     return NULL;
 650 }
 651 
 652 void
 653 create_notifications(pe_resource_t * rsc, notify_data_t * n_data, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 654 {
 655     GList *gIter = NULL;
 656     pe_action_t *stop = NULL;
 657     pe_action_t *start = NULL;
 658     enum action_tasks task = text2task(n_data->action);
 659 
 660     if (rsc->children) {
 661         gIter = rsc->children;
 662         for (; gIter != NULL; gIter = gIter->next) {
 663             pe_resource_t *child = (pe_resource_t *) gIter->data;
 664 
 665             create_notifications(child, n_data, data_set);
 666         }
 667         return;
 668     }
 669 
 670     /* Copy notification details into standard ops */
 671 
 672     for (gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
 673         pe_action_t *op = (pe_action_t *) gIter->data;
 674 
 675         if (!pcmk_is_set(op->flags, pe_action_optional)
 676             && (op->node != NULL)) {
 677 
 678             enum action_tasks t = text2task(op->task);
 679 
 680             switch (t) {
 681                 case start_rsc:
 682                 case stop_rsc:
 683                 case action_promote:
 684                 case action_demote:
 685                     add_notify_data_to_action_meta(n_data, op);
 686                     break;
 687                 default:
 688                     break;
 689             }
 690         }
 691     }
 692 
 693     switch (task) {
 694         case start_rsc:
 695             if (n_data->start == NULL) {
 696                 pe_rsc_trace(rsc, "Skipping empty notification for: %s.%s (%s->%s)",
 697                              n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
 698                 return;
 699             }
 700             break;
 701         case action_promote:
 702             if (n_data->promote == NULL) {
 703                 pe_rsc_trace(rsc, "Skipping empty notification for: %s.%s (%s->%s)",
 704                              n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
 705                 return;
 706             }
 707             break;
 708         case action_demote:
 709             if (n_data->demote == NULL) {
 710                 pe_rsc_trace(rsc, "Skipping empty notification for: %s.%s (%s->%s)",
 711                              n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
 712                 return;
 713             }
 714             break;
 715         default:
 716             /* We cannot do the same for stop_rsc/n_data->stop at it
 717              * might be implied by fencing
 718              */
 719             break;
 720     }
 721 
 722     pe_rsc_trace(rsc, "Creating notifications for: %s.%s (%s->%s)",
 723                  n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
 724 
 725     stop = find_first_action(rsc->actions, NULL, RSC_STOP, NULL);
 726     start = find_first_action(rsc->actions, NULL, RSC_START, NULL);
 727 
 728     /* stop / demote */
 729     if (rsc->role != RSC_ROLE_STOPPED) {
 730         if (task == stop_rsc || task == action_demote) {
 731             gIter = rsc->running_on;
 732             for (; gIter != NULL; gIter = gIter->next) {
 733                 pe_node_t *current_node = (pe_node_t *) gIter->data;
 734 
 735                 /* if this stop action is a pseudo action as a result of the current
 736                  * node being fenced, this stop action is implied by the fencing 
 737                  * action. There's no reason to send the fenced node a stop notification */ 
 738                 if (stop && pcmk_is_set(stop->flags, pe_action_pseudo) &&
 739                     (current_node->details->unclean || current_node->details->remote_requires_reset) ) {
 740 
 741                     continue;
 742                 }
 743 
 744                 pe_notify(rsc, current_node, n_data->pre, n_data->pre_done, n_data, data_set);
 745                 if (task == action_demote || stop == NULL
 746                     || pcmk_is_set(stop->flags, pe_action_optional)) {
 747                     pe_post_notify(rsc, current_node, n_data, data_set);
 748                 }
 749             }
 750         }
 751     }
 752 
 753     /* start / promote */
 754     if (rsc->next_role != RSC_ROLE_STOPPED) {
 755         if (rsc->allocated_to == NULL) {
 756             pe_proc_err("Next role '%s' but %s is not allocated", role2text(rsc->next_role),
 757                         rsc->id);
 758 
 759         } else if (task == start_rsc || task == action_promote) {
 760 
 761             if (start) {
 762                 pe_action_t *remote_start = find_remote_start(start);
 763 
 764                 if (remote_start
 765                     && !pcmk_is_set(remote_start->flags, pe_action_runnable)) {
 766                     /* Start and promote actions for a clone instance behind
 767                      * a Pacemaker Remote connection happen after the
 768                      * connection starts. If the connection start is blocked, do
 769                      * not schedule notifications for these actions.
 770                      */
 771                     return;
 772                 }
 773             }
 774             if ((task != start_rsc) || (start == NULL)
 775                 || pcmk_is_set(start->flags, pe_action_optional)) {
 776 
 777                 pe_notify(rsc, rsc->allocated_to, n_data->pre, n_data->pre_done, n_data, data_set);
 778             }
 779             pe_post_notify(rsc, rsc->allocated_to, n_data, data_set);
 780         }
 781     }
 782 }
 783 
 784 void
 785 free_notification_data(notify_data_t * n_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 786 {
 787     if (n_data == NULL) {
 788         return;
 789     }
 790 
 791     g_list_free_full(n_data->stop, free);
 792     g_list_free_full(n_data->start, free);
 793     g_list_free_full(n_data->demote, free);
 794     g_list_free_full(n_data->promote, free);
 795     g_list_free_full(n_data->promoted, free);
 796     g_list_free_full(n_data->unpromoted, free);
 797     g_list_free_full(n_data->active, free);
 798     g_list_free_full(n_data->inactive, free);
 799     pcmk_free_nvpairs(n_data->keys);
 800     free(n_data);
 801 }
 802 
 803 void
 804 create_secondary_notification(pe_action_t *action, pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 805                               pe_action_t *stonith_op,
 806                               pe_working_set_t *data_set)
 807 {
 808     notify_data_t *n_data;
 809 
 810     crm_info("Creating secondary notification for %s", action->uuid);
 811     n_data = create_notification_boundaries(rsc, RSC_STOP, NULL, stonith_op,
 812                                             data_set);
 813     collect_notification_data(rsc, TRUE, FALSE, n_data);
 814     add_notify_env(n_data, "notify_stop_resource", rsc->id);
 815     add_notify_env(n_data, "notify_stop_uname", action->node->details->uname);
 816     create_notifications(uber_parent(rsc), n_data, data_set);
 817     free_notification_data(n_data);
 818 }

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