LCOV - code coverage report
Current view: top level - pengine - pe_notif.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 417 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 18 0.0 %

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

Generated by: LCOV version 1.14