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         pe__set_action_flags(n_data->pre, pe_action_pseudo|pe_action_runnable);
 320 
 321         add_hash_param(n_data->pre->meta, "notify_type", "pre");
 322         add_hash_param(n_data->pre->meta, "notify_operation", n_data->action);
 323 
 324         add_hash_param(n_data->pre->meta, "notify_key_type", "pre");
 325         add_hash_param(n_data->pre->meta, "notify_key_operation", start->task);
 326 
 327         /* create pre_notify_complete */
 328         key = pcmk__notify_key(rsc->id, "confirmed-pre", start->task);
 329         n_data->pre_done = custom_action(rsc, key, RSC_NOTIFIED, NULL,
 330                                          pcmk_is_set(start->flags, pe_action_optional),
 331                                          TRUE, data_set);
 332         pe__set_action_flags(n_data->pre_done,
 333                              pe_action_pseudo|pe_action_runnable);
 334 
 335         add_hash_param(n_data->pre_done->meta, "notify_type", "pre");
 336         add_hash_param(n_data->pre_done->meta, "notify_operation", n_data->action);
 337 
 338         add_hash_param(n_data->pre_done->meta, "notify_key_type", "confirmed-pre");
 339         add_hash_param(n_data->pre_done->meta, "notify_key_operation", start->task);
 340 
 341         order_actions(n_data->pre_done, start, pe_order_optional);
 342         order_actions(n_data->pre, n_data->pre_done, pe_order_optional);
 343     }
 344 
 345     if (end) {
 346         /* create post-event notification wrappers */
 347         key = pcmk__notify_key(rsc->id, "post", end->task);
 348         n_data->post = custom_action(rsc, key, RSC_NOTIFY, NULL,
 349                                      pcmk_is_set(end->flags, pe_action_optional),
 350                                      TRUE, data_set);
 351 
 352         n_data->post->priority = INFINITY;
 353         pe__set_action_flags(n_data->post, pe_action_pseudo);
 354         if (pcmk_is_set(end->flags, pe_action_runnable)) {
 355             pe__set_action_flags(n_data->post, pe_action_runnable);
 356         } else {
 357             pe__clear_action_flags(n_data->post, pe_action_runnable);
 358         }
 359 
 360         add_hash_param(n_data->post->meta, "notify_type", "post");
 361         add_hash_param(n_data->post->meta, "notify_operation", n_data->action);
 362 
 363         add_hash_param(n_data->post->meta, "notify_key_type", "post");
 364         add_hash_param(n_data->post->meta, "notify_key_operation", end->task);
 365 
 366         /* create post_notify_complete */
 367         key = pcmk__notify_key(rsc->id, "confirmed-post", end->task);
 368         n_data->post_done = custom_action(rsc, key, RSC_NOTIFIED, NULL,
 369                                           pcmk_is_set(end->flags, pe_action_optional),
 370                                           TRUE, data_set);
 371 
 372         n_data->post_done->priority = INFINITY;
 373         pe__set_action_flags(n_data->post_done, pe_action_pseudo);
 374         if (pcmk_is_set(end->flags, pe_action_runnable)) {
 375             pe__set_action_flags(n_data->post_done, pe_action_runnable);
 376         } else {
 377             pe__clear_action_flags(n_data->post_done, pe_action_runnable);
 378         }
 379 
 380         add_hash_param(n_data->post_done->meta, "notify_type", "post");
 381         add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action);
 382 
 383         add_hash_param(n_data->post_done->meta, "notify_key_type", "confirmed-post");
 384         add_hash_param(n_data->post_done->meta, "notify_key_operation", end->task);
 385 
 386         order_actions(end, n_data->post, pe_order_implies_then);
 387         order_actions(n_data->post, n_data->post_done, pe_order_implies_then);
 388     }
 389 
 390     if (start && end) {
 391         order_actions(n_data->pre_done, n_data->post, pe_order_optional);
 392     }
 393     return n_data;
 394 }
 395 
 396 void
 397 collect_notification_data(pe_resource_t * rsc, gboolean state, gboolean activity,
     /* [previous][next][first][last][top][bottom][index][help] */
 398                           notify_data_t * n_data)
 399 {
 400 
 401     if(n_data->allowed_nodes == NULL) {
 402         n_data->allowed_nodes = rsc->allowed_nodes;
 403     }
 404 
 405     if (rsc->children) {
 406         GList *gIter = rsc->children;
 407 
 408         for (; gIter != NULL; gIter = gIter->next) {
 409             pe_resource_t *child = (pe_resource_t *) gIter->data;
 410 
 411             collect_notification_data(child, state, activity, n_data);
 412         }
 413         return;
 414     }
 415 
 416     if (state) {
 417         notify_entry_t *entry = NULL;
 418 
 419         entry = calloc(1, sizeof(notify_entry_t));
 420         entry->rsc = rsc;
 421         if (rsc->running_on) {
 422             /* we only take the first one */
 423             entry->node = rsc->running_on->data;
 424         }
 425 
 426         pe_rsc_trace(rsc, "%s state: %s", rsc->id, role2text(rsc->role));
 427 
 428         switch (rsc->role) {
 429             case RSC_ROLE_STOPPED:
 430                 n_data->inactive = g_list_prepend(n_data->inactive, entry);
 431                 break;
 432             case RSC_ROLE_STARTED:
 433                 n_data->active = g_list_prepend(n_data->active, entry);
 434                 break;
 435             case RSC_ROLE_UNPROMOTED:
 436                 n_data->unpromoted = g_list_prepend(n_data->unpromoted, entry);
 437                 n_data->active = g_list_prepend(n_data->active,
 438                                                 dup_notify_entry(entry));
 439                 break;
 440             case RSC_ROLE_PROMOTED:
 441                 n_data->promoted = g_list_prepend(n_data->promoted, entry);
 442                 n_data->active = g_list_prepend(n_data->active,
 443                                                 dup_notify_entry(entry));
 444                 break;
 445             default:
 446                 crm_err("Unsupported notify role");
 447                 free(entry);
 448                 break;
 449         }
 450     }
 451 
 452     if (activity) {
 453         notify_entry_t *entry = NULL;
 454         enum action_tasks task;
 455 
 456         GList *gIter = rsc->actions;
 457 
 458         for (; gIter != NULL; gIter = gIter->next) {
 459             pe_action_t *op = (pe_action_t *) gIter->data;
 460 
 461             if (!pcmk_is_set(op->flags, pe_action_optional)
 462                 && (op->node != NULL)) {
 463 
 464                 task = text2task(op->task);
 465 
 466                 if(task == stop_rsc && op->node->details->unclean) {
 467                     // Create anyway (additional noise if node can't be fenced)
 468                 } else if (!pcmk_is_set(op->flags, pe_action_runnable)) {
 469                     continue;
 470                 }
 471 
 472                 entry = calloc(1, sizeof(notify_entry_t));
 473                 entry->node = op->node;
 474                 entry->rsc = rsc;
 475 
 476                 switch (task) {
 477                     case start_rsc:
 478                         n_data->start = g_list_prepend(n_data->start, entry);
 479                         break;
 480                     case stop_rsc:
 481                         n_data->stop = g_list_prepend(n_data->stop, entry);
 482                         break;
 483                     case action_promote:
 484                         n_data->promote = g_list_prepend(n_data->promote, entry);
 485                         break;
 486                     case action_demote:
 487                         n_data->demote = g_list_prepend(n_data->demote, entry);
 488                         break;
 489                     default:
 490                         free(entry);
 491                         break;
 492                 }
 493             }
 494         }
 495     }
 496 }
 497 
 498 #define add_notify_env(n_data, key, value) do {                         \
 499          n_data->keys = pcmk_prepend_nvpair(n_data->keys, key, value);  \
 500     } while (0)
 501 
 502 #define add_notify_env_free(n_data, key, value) do {                    \
 503          n_data->keys = pcmk_prepend_nvpair(n_data->keys, key, value);  \
 504          free(value); value = NULL;                                     \
 505     } while (0)
 506 
 507 /*!
 508  * \internal
 509  * \brief Create notification name/value pairs from raw data
 510  *
 511  * \param[in]     rsc       Resource that notification is for
 512  * \param[in,out] n_data    Notification data
 513  * \param[in]     data_set  Cluster working set
 514  */
 515 void
 516 pcmk__create_notification_keys(pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 517                                notify_data_t *n_data,
 518                                pe_working_set_t *data_set)
 519 {
 520     bool required = false; // Whether to make notify actions required
 521     char *rsc_list = NULL;
 522     char *node_list = NULL;
 523     char *metal_list = NULL;
 524     const char *source = NULL;
 525     GList *nodes = NULL;
 526 
 527     n_data->stop = expand_list(n_data->stop, &rsc_list, &node_list);
 528     if (!pcmk__str_eq(" ", rsc_list, pcmk__str_null_matches)
 529         && pcmk__str_eq(n_data->action, RSC_STOP, pcmk__str_casei)) {
 530         required = true;
 531     }
 532     add_notify_env_free(n_data, "notify_stop_resource", rsc_list);
 533     add_notify_env_free(n_data, "notify_stop_uname", node_list);
 534 
 535     if ((n_data->start != NULL)
 536         && pcmk__str_eq(n_data->action, RSC_START, pcmk__str_casei)) {
 537         required = true;
 538     }
 539     n_data->start = expand_list(n_data->start, &rsc_list, &node_list);
 540     add_notify_env_free(n_data, "notify_start_resource", rsc_list);
 541     add_notify_env_free(n_data, "notify_start_uname", node_list);
 542 
 543     if ((n_data->demote != NULL)
 544         && pcmk__str_eq(n_data->action, RSC_DEMOTE, pcmk__str_casei)) {
 545         required = true;
 546     }
 547     n_data->demote = expand_list(n_data->demote, &rsc_list, &node_list);
 548     add_notify_env_free(n_data, "notify_demote_resource", rsc_list);
 549     add_notify_env_free(n_data, "notify_demote_uname", node_list);
 550 
 551     if ((n_data->promote != NULL)
 552         && pcmk__str_eq(n_data->action, RSC_PROMOTE, pcmk__str_casei)) {
 553         required = true;
 554     }
 555     n_data->promote = expand_list(n_data->promote, &rsc_list, &node_list);
 556     add_notify_env_free(n_data, "notify_promote_resource", rsc_list);
 557     add_notify_env_free(n_data, "notify_promote_uname", node_list);
 558 
 559     n_data->active = expand_list(n_data->active, &rsc_list, &node_list);
 560     add_notify_env_free(n_data, "notify_active_resource", rsc_list);
 561     add_notify_env_free(n_data, "notify_active_uname", node_list);
 562 
 563     n_data->unpromoted = expand_list(n_data->unpromoted, &rsc_list, &node_list);
 564     add_notify_env(n_data, "notify_unpromoted_resource", rsc_list);
 565     add_notify_env(n_data, "notify_unpromoted_uname", node_list);
 566 
 567     // Deprecated: kept for backward compatibility with older resource agents
 568     add_notify_env_free(n_data, "notify_slave_resource", rsc_list);
 569     add_notify_env_free(n_data, "notify_slave_uname", node_list);
 570 
 571     n_data->promoted = expand_list(n_data->promoted, &rsc_list, &node_list);
 572     add_notify_env(n_data, "notify_promoted_resource", rsc_list);
 573     add_notify_env(n_data, "notify_promoted_uname", node_list);
 574 
 575     // Deprecated: kept for backward compatibility with older resource agents
 576     add_notify_env_free(n_data, "notify_master_resource", rsc_list);
 577     add_notify_env_free(n_data, "notify_master_uname", node_list);
 578 
 579     n_data->inactive = expand_list(n_data->inactive, &rsc_list, NULL);
 580     add_notify_env_free(n_data, "notify_inactive_resource", rsc_list);
 581 
 582     nodes = g_hash_table_get_values(n_data->allowed_nodes);
 583     if (!pcmk__is_daemon) {
 584         /* If printing to stdout, sort the node list, for consistent
 585          * regression test output (while avoiding the performance hit
 586          * for the live cluster).
 587          */
 588         nodes = g_list_sort(nodes, sort_node_uname);
 589     }
 590     expand_node_list(nodes, &node_list, NULL);
 591     add_notify_env_free(n_data, "notify_available_uname", node_list);
 592     g_list_free(nodes);
 593 
 594     source = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET);
 595     if (pcmk__str_eq("host", source, pcmk__str_casei)) {
 596         expand_node_list(data_set->nodes, &node_list, &metal_list);
 597         add_notify_env_free(n_data, "notify_all_hosts", metal_list);
 598     } else {
 599         expand_node_list(data_set->nodes, &node_list, NULL);
 600     }
 601     add_notify_env_free(n_data, "notify_all_uname", node_list);
 602 
 603     if (required && n_data->pre) {
 604         pe__clear_action_flags(n_data->pre, pe_action_optional);
 605         pe__clear_action_flags(n_data->pre_done, pe_action_optional);
 606     }
 607 
 608     if (required && n_data->post) {
 609         pe__clear_action_flags(n_data->post, pe_action_optional);
 610         pe__clear_action_flags(n_data->post_done, pe_action_optional);
 611     }
 612 }
 613 
 614 /*
 615  * \internal
 616  * \brief Find any remote connection start relevant to an action
 617  *
 618  * \param[in] action  Action to chek
 619  *
 620  * \return If action is behind a remote connection, connection's start
 621  */
 622 static pe_action_t *
 623 find_remote_start(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 624 {
 625     if (action && action->node) {
 626         pe_resource_t *remote_rsc = action->node->details->remote_rsc;
 627 
 628         if (remote_rsc) {
 629             return find_first_action(remote_rsc->actions, NULL, RSC_START,
 630                                      NULL);
 631         }
 632     }
 633     return NULL;
 634 }
 635 
 636 void
 637 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] */
 638 {
 639     GList *gIter = NULL;
 640     pe_action_t *stop = NULL;
 641     pe_action_t *start = NULL;
 642     enum action_tasks task = text2task(n_data->action);
 643 
 644     if (rsc->children) {
 645         gIter = rsc->children;
 646         for (; gIter != NULL; gIter = gIter->next) {
 647             pe_resource_t *child = (pe_resource_t *) gIter->data;
 648 
 649             create_notifications(child, n_data, data_set);
 650         }
 651         return;
 652     }
 653 
 654     /* Copy notification details into standard ops */
 655 
 656     for (gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
 657         pe_action_t *op = (pe_action_t *) gIter->data;
 658 
 659         if (!pcmk_is_set(op->flags, pe_action_optional)
 660             && (op->node != NULL)) {
 661 
 662             enum action_tasks t = text2task(op->task);
 663 
 664             switch (t) {
 665                 case start_rsc:
 666                 case stop_rsc:
 667                 case action_promote:
 668                 case action_demote:
 669                     add_notify_data_to_action_meta(n_data, op);
 670                     break;
 671                 default:
 672                     break;
 673             }
 674         }
 675     }
 676 
 677     switch (task) {
 678         case start_rsc:
 679             if (n_data->start == NULL) {
 680                 pe_rsc_trace(rsc, "Skipping empty notification for: %s.%s (%s->%s)",
 681                              n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
 682                 return;
 683             }
 684             break;
 685         case action_promote:
 686             if (n_data->promote == NULL) {
 687                 pe_rsc_trace(rsc, "Skipping empty notification for: %s.%s (%s->%s)",
 688                              n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
 689                 return;
 690             }
 691             break;
 692         case action_demote:
 693             if (n_data->demote == NULL) {
 694                 pe_rsc_trace(rsc, "Skipping empty notification for: %s.%s (%s->%s)",
 695                              n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
 696                 return;
 697             }
 698             break;
 699         default:
 700             /* We cannot do the same for stop_rsc/n_data->stop at it
 701              * might be implied by fencing
 702              */
 703             break;
 704     }
 705 
 706     pe_rsc_trace(rsc, "Creating notifications for: %s.%s (%s->%s)",
 707                  n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
 708 
 709     stop = find_first_action(rsc->actions, NULL, RSC_STOP, NULL);
 710     start = find_first_action(rsc->actions, NULL, RSC_START, NULL);
 711 
 712     /* stop / demote */
 713     if (rsc->role != RSC_ROLE_STOPPED) {
 714         if (task == stop_rsc || task == action_demote) {
 715             gIter = rsc->running_on;
 716             for (; gIter != NULL; gIter = gIter->next) {
 717                 pe_node_t *current_node = (pe_node_t *) gIter->data;
 718 
 719                 /* if this stop action is a pseudo action as a result of the current
 720                  * node being fenced, this stop action is implied by the fencing 
 721                  * action. There's no reason to send the fenced node a stop notification */ 
 722                 if (stop && pcmk_is_set(stop->flags, pe_action_pseudo) &&
 723                     (current_node->details->unclean || current_node->details->remote_requires_reset) ) {
 724 
 725                     continue;
 726                 }
 727 
 728                 pe_notify(rsc, current_node, n_data->pre, n_data->pre_done, n_data, data_set);
 729                 if (task == action_demote || stop == NULL
 730                     || pcmk_is_set(stop->flags, pe_action_optional)) {
 731                     pe_post_notify(rsc, current_node, n_data, data_set);
 732                 }
 733             }
 734         }
 735     }
 736 
 737     /* start / promote */
 738     if (rsc->next_role != RSC_ROLE_STOPPED) {
 739         if (rsc->allocated_to == NULL) {
 740             pe_proc_err("Next role '%s' but %s is not allocated", role2text(rsc->next_role),
 741                         rsc->id);
 742 
 743         } else if (task == start_rsc || task == action_promote) {
 744 
 745             if (start) {
 746                 pe_action_t *remote_start = find_remote_start(start);
 747 
 748                 if (remote_start
 749                     && !pcmk_is_set(remote_start->flags, pe_action_runnable)) {
 750                     /* Start and promote actions for a clone instance behind
 751                      * a Pacemaker Remote connection happen after the
 752                      * connection starts. If the connection start is blocked, do
 753                      * not schedule notifications for these actions.
 754                      */
 755                     return;
 756                 }
 757             }
 758             if ((task != start_rsc) || (start == NULL)
 759                 || pcmk_is_set(start->flags, pe_action_optional)) {
 760 
 761                 pe_notify(rsc, rsc->allocated_to, n_data->pre, n_data->pre_done, n_data, data_set);
 762             }
 763             pe_post_notify(rsc, rsc->allocated_to, n_data, data_set);
 764         }
 765     }
 766 }
 767 
 768 void
 769 free_notification_data(notify_data_t * n_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 770 {
 771     if (n_data == NULL) {
 772         return;
 773     }
 774 
 775     g_list_free_full(n_data->stop, free);
 776     g_list_free_full(n_data->start, free);
 777     g_list_free_full(n_data->demote, free);
 778     g_list_free_full(n_data->promote, free);
 779     g_list_free_full(n_data->promoted, free);
 780     g_list_free_full(n_data->unpromoted, free);
 781     g_list_free_full(n_data->active, free);
 782     g_list_free_full(n_data->inactive, free);
 783     pcmk_free_nvpairs(n_data->keys);
 784     free(n_data);
 785 }
 786 
 787 void
 788 create_secondary_notification(pe_action_t *action, pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 789                               pe_action_t *stonith_op,
 790                               pe_working_set_t *data_set)
 791 {
 792     notify_data_t *n_data;
 793 
 794     crm_info("Creating secondary notification for %s", action->uuid);
 795     n_data = create_notification_boundaries(rsc, RSC_STOP, NULL, stonith_op,
 796                                             data_set);
 797     collect_notification_data(rsc, TRUE, FALSE, n_data);
 798     add_notify_env(n_data, "notify_stop_resource", rsc->id);
 799     add_notify_env(n_data, "notify_stop_uname", action->node->details->uname);
 800     create_notifications(uber_parent(rsc), n_data, data_set);
 801     free_notification_data(n_data);
 802 }

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