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