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