root/lib/lrmd/lrmd_alerts.c

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

DEFINITIONS

This source file includes following definitions.
  1. alert_key2param
  2. alert_key2param_int
  3. alert_key2param_ms
  4. set_ev_kv
  5. alert_envvar2params
  6. is_target_alert
  7. exec_alert_list
  8. lrmd_send_attribute_alert
  9. lrmd_send_node_alert
  10. lrmd_send_fencing_alert
  11. lrmd_send_resource_alert

   1 /*
   2  * Copyright 2015-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 Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <glib.h>
  13 #include <unistd.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/common/xml.h>
  17 #include <crm/services.h>
  18 #include <crm/common/mainloop.h>
  19 #include <crm/common/alerts_internal.h>
  20 #include <crm/lrmd_internal.h>
  21 
  22 #include <crm/pengine/status.h>
  23 #include <crm/cib.h>
  24 #include <crm/lrmd.h>
  25 
  26 static lrmd_key_value_t *
  27 alert_key2param(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
     /* [previous][next][first][last][top][bottom][index][help] */
  28                 const char *value)
  29 {
  30     if (value == NULL) {
  31         value = "";
  32     }
  33     crm_trace("Setting alert key %s = '%s'", pcmk__alert_keys[name], value);
  34     return lrmd_key_value_add(head, pcmk__alert_keys[name], value);
  35 }
  36 
  37 static lrmd_key_value_t *
  38 alert_key2param_int(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
     /* [previous][next][first][last][top][bottom][index][help] */
  39                     int value)
  40 {
  41     char *value_s = pcmk__itoa(value);
  42 
  43     head = alert_key2param(head, name, value_s);
  44     free(value_s);
  45     return head;
  46 }
  47 
  48 static lrmd_key_value_t *
  49 alert_key2param_ms(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
     /* [previous][next][first][last][top][bottom][index][help] */
  50                    guint value)
  51 {
  52     char *value_s = crm_strdup_printf("%u", value);
  53 
  54     head = alert_key2param(head, name, value_s);
  55     free(value_s);
  56     return head;
  57 }
  58 
  59 static void
  60 set_ev_kv(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  61 {
  62     lrmd_key_value_t **head = (lrmd_key_value_t **) user_data;
  63 
  64     if (value) {
  65         crm_trace("Setting environment variable %s='%s'",
  66                   (char*)key, (char*)value);
  67         *head = lrmd_key_value_add(*head, key, value);
  68     }
  69 }
  70 
  71 static lrmd_key_value_t *
  72 alert_envvar2params(lrmd_key_value_t *head, const pcmk__alert_t *entry)
     /* [previous][next][first][last][top][bottom][index][help] */
  73 {
  74     if (entry->envvars) {
  75         g_hash_table_foreach(entry->envvars, set_ev_kv, &head);
  76     }
  77     return head;
  78 }
  79 
  80 /*
  81  * We could use g_strv_contains() instead of this function,
  82  * but that has only been available since glib 2.43.2.
  83  */
  84 static gboolean
  85 is_target_alert(char **list, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
  86 {
  87     int target_list_num = 0;
  88     gboolean rc = FALSE;
  89 
  90     CRM_CHECK(value != NULL, return FALSE);
  91 
  92     if (list == NULL) {
  93         return TRUE;
  94     }
  95 
  96     target_list_num = g_strv_length(list);
  97 
  98     for (int cnt = 0; cnt < target_list_num; cnt++) {
  99         if (strcmp(list[cnt], value) == 0) {
 100             rc = TRUE;
 101             break;
 102         }
 103     }
 104     return rc;
 105 }
 106 
 107 /*!
 108  * \internal
 109  * \brief Execute alert agents for an event
 110  *
 111  * \param[in,out] lrmd        Executor connection to use
 112  * \param[in]     alert_list  Alerts to execute
 113  * \param[in]     kind        Type of event that is being alerted for
 114  * \param[in]     attr_name   If pcmk__alert_attribute, the attribute name
 115  * \param[in,out] params      Environment variables to pass to agents
 116  *
 117  * \retval pcmk_ok on success
 118  * \retval -1 if some alerts failed
 119  * \retval -2 if all alerts failed
 120  */
 121 static int
 122 exec_alert_list(lrmd_t *lrmd, const GList *alert_list,
     /* [previous][next][first][last][top][bottom][index][help] */
 123                 enum pcmk__alert_flags kind, const char *attr_name,
 124                 lrmd_key_value_t *params)
 125 {
 126     bool any_success = FALSE, any_failure = FALSE;
 127     const char *kind_s = pcmk__alert_flag2text(kind);
 128     pcmk__time_hr_t *now = NULL;
 129     char timestamp_epoch[20];
 130     char timestamp_usec[7];
 131     time_t epoch = 0;
 132 
 133     params = alert_key2param(params, PCMK__alert_key_kind, kind_s);
 134     params = alert_key2param(params, PCMK__alert_key_version,
 135                              PACEMAKER_VERSION);
 136 
 137     for (const GList *iter = alert_list;
 138          iter != NULL; iter = g_list_next(iter)) {
 139         const pcmk__alert_t *entry = (pcmk__alert_t *) (iter->data);
 140         lrmd_key_value_t *copy_params = NULL;
 141         lrmd_key_value_t *head = NULL;
 142         int rc;
 143 
 144         if (!pcmk_is_set(entry->flags, kind)) {
 145             crm_trace("Filtering unwanted %s alert to %s via %s",
 146                       kind_s, entry->recipient, entry->id);
 147             continue;
 148         }
 149 
 150         if ((kind == pcmk__alert_attribute)
 151             && !is_target_alert(entry->select_attribute_name, attr_name)) {
 152 
 153             crm_trace("Filtering unwanted attribute '%s' alert to %s via %s",
 154                       attr_name, entry->recipient, entry->id);
 155             continue;
 156         }
 157 
 158         if (now == NULL) {
 159             now = pcmk__time_hr_now(&epoch);
 160         }
 161         crm_info("Sending %s alert via %s to %s",
 162                  kind_s, entry->id, entry->recipient);
 163 
 164         /* Make a copy of the parameters, because each alert will be unique */
 165         for (head = params; head != NULL; head = head->next) {
 166             copy_params = lrmd_key_value_add(copy_params, head->key, head->value);
 167         }
 168 
 169         copy_params = alert_key2param(copy_params, PCMK__alert_key_recipient,
 170                                       entry->recipient);
 171 
 172         if (now) {
 173             char *timestamp = pcmk__time_format_hr(entry->tstamp_format, now);
 174 
 175             if (timestamp) {
 176                 copy_params = alert_key2param(copy_params,
 177                                               PCMK__alert_key_timestamp,
 178                                               timestamp);
 179                 free(timestamp);
 180             }
 181 
 182             snprintf(timestamp_epoch, sizeof(timestamp_epoch), "%lld",
 183                      (long long) epoch);
 184             copy_params = alert_key2param(copy_params,
 185                                           PCMK__alert_key_timestamp_epoch,
 186                                           timestamp_epoch);
 187             snprintf(timestamp_usec, sizeof(timestamp_usec), "%06d", now->useconds);
 188             copy_params = alert_key2param(copy_params,
 189                                           PCMK__alert_key_timestamp_usec,
 190                                           timestamp_usec);
 191         }
 192 
 193         copy_params = alert_envvar2params(copy_params, entry);
 194 
 195         rc = lrmd->cmds->exec_alert(lrmd, entry->id, entry->path,
 196                                     entry->timeout, copy_params);
 197         if (rc < 0) {
 198             crm_err("Could not execute alert %s: %s " QB_XS " rc=%d",
 199                     entry->id, pcmk_strerror(rc), rc);
 200             any_failure = TRUE;
 201         } else {
 202             any_success = TRUE;
 203         }
 204     }
 205 
 206     if (now) {
 207         free(now);
 208     }
 209 
 210     if (any_failure) {
 211         return (any_success? -1 : -2);
 212     }
 213     return pcmk_ok;
 214 }
 215 
 216 /*!
 217  * \internal
 218  * \brief Send an alert for a node attribute change
 219  *
 220  * \param[in,out] lrmd        Executor connection to use
 221  * \param[in]     alert_list  List of alert agents to execute
 222  * \param[in]     node        Name of node with attribute change
 223  * \param[in]     nodeid      Node ID of node with attribute change
 224  * \param[in]     attr_name   Name of attribute that changed
 225  * \param[in]     attr_value  New value of attribute that changed
 226  *
 227  * \retval pcmk_ok on success
 228  * \retval -1 if some alert agents failed
 229  * \retval -2 if all alert agents failed
 230  */
 231 int
 232 lrmd_send_attribute_alert(lrmd_t *lrmd, const GList *alert_list,
     /* [previous][next][first][last][top][bottom][index][help] */
 233                           const char *node, uint32_t nodeid,
 234                           const char *attr_name, const char *attr_value)
 235 {
 236     int rc = pcmk_ok;
 237     lrmd_key_value_t *params = NULL;
 238 
 239     if (lrmd == NULL) {
 240         return -2;
 241     }
 242 
 243     params = alert_key2param(params, PCMK__alert_key_node, node);
 244     params = alert_key2param_int(params, PCMK__alert_key_nodeid, nodeid);
 245     params = alert_key2param(params, PCMK__alert_key_attribute_name, attr_name);
 246     params = alert_key2param(params, PCMK__alert_key_attribute_value,
 247                              attr_value);
 248 
 249     rc = exec_alert_list(lrmd, alert_list, pcmk__alert_attribute, attr_name,
 250                          params);
 251     lrmd_key_value_freeall(params);
 252     return rc;
 253 }
 254 
 255 /*!
 256  * \internal
 257  * \brief Send an alert for a node membership event
 258  *
 259  * \param[in,out] lrmd        Executor connection to use
 260  * \param[in]     alert_list  List of alert agents to execute
 261  * \param[in]     node        Name of node with change
 262  * \param[in]     nodeid      Node ID of node with change
 263  * \param[in]     state       New state of node with change
 264  *
 265  * \retval pcmk_ok on success
 266  * \retval -1 if some alert agents failed
 267  * \retval -2 if all alert agents failed
 268  */
 269 int
 270 lrmd_send_node_alert(lrmd_t *lrmd, const GList *alert_list,
     /* [previous][next][first][last][top][bottom][index][help] */
 271                      const char *node, uint32_t nodeid, const char *state)
 272 {
 273     int rc = pcmk_ok;
 274     lrmd_key_value_t *params = NULL;
 275 
 276     if (lrmd == NULL) {
 277         return -2;
 278     }
 279 
 280     params = alert_key2param(params, PCMK__alert_key_node, node);
 281     params = alert_key2param(params, PCMK__alert_key_desc, state);
 282     params = alert_key2param_int(params, PCMK__alert_key_nodeid, nodeid);
 283 
 284     rc = exec_alert_list(lrmd, alert_list, pcmk__alert_node, NULL, params);
 285     lrmd_key_value_freeall(params);
 286     return rc;
 287 }
 288 
 289 /*!
 290  * \internal
 291  * \brief Send an alert for a fencing event
 292  *
 293  * \param[in,out] lrmd        Executor connection to use
 294  * \param[in]     alert_list  List of alert agents to execute
 295  * \param[in]     target      Name of fence target node
 296  * \param[in]     task        Type of fencing event that occurred
 297  * \param[in]     desc        Readable description of event
 298  * \param[in]     op_rc       Result of fence action
 299  *
 300  * \retval pcmk_ok on success
 301  * \retval -1 if some alert agents failed
 302  * \retval -2 if all alert agents failed
 303  */
 304 int
 305 lrmd_send_fencing_alert(lrmd_t *lrmd, const GList *alert_list,
     /* [previous][next][first][last][top][bottom][index][help] */
 306                         const char *target, const char *task, const char *desc,
 307                         int op_rc)
 308 {
 309     int rc = pcmk_ok;
 310     lrmd_key_value_t *params = NULL;
 311 
 312     if (lrmd == NULL) {
 313         return -2;
 314     }
 315 
 316     params = alert_key2param(params, PCMK__alert_key_node, target);
 317     params = alert_key2param(params, PCMK__alert_key_task, task);
 318     params = alert_key2param(params, PCMK__alert_key_desc, desc);
 319     params = alert_key2param_int(params, PCMK__alert_key_rc, op_rc);
 320 
 321     rc = exec_alert_list(lrmd, alert_list, pcmk__alert_fencing, NULL, params);
 322     lrmd_key_value_freeall(params);
 323     return rc;
 324 }
 325 
 326 /*!
 327  * \internal
 328  * \brief Send an alert for a resource operation
 329  *
 330  * \param[in,out] lrmd        Executor connection to use
 331  * \param[in]     alert_list  List of alert agents to execute
 332  * \param[in]     node        Name of node that executed operation
 333  * \param[in]     op          Resource operation
 334  *
 335  * \retval pcmk_ok on success
 336  * \retval -1 if some alert agents failed
 337  * \retval -2 if all alert agents failed
 338  */
 339 int
 340 lrmd_send_resource_alert(lrmd_t *lrmd, const GList *alert_list,
     /* [previous][next][first][last][top][bottom][index][help] */
 341                          const char *node, const lrmd_event_data_t *op)
 342 {
 343     int rc = pcmk_ok;
 344     int target_rc = pcmk_ok;
 345     lrmd_key_value_t *params = NULL;
 346 
 347     if (lrmd == NULL) {
 348         return -2;
 349     }
 350 
 351     target_rc = rsc_op_expected_rc(op);
 352     if ((op->interval_ms == 0) && (target_rc == op->rc)
 353         && pcmk__str_eq(op->op_type, PCMK_ACTION_MONITOR, pcmk__str_casei)) {
 354 
 355         /* Don't send alerts for probes with the expected result. Leave it up to
 356          * the agent whether to alert for 'failed' probes. (Even if we find a
 357          * resource running, it was probably because someone did a clean-up of
 358          * the status section.)
 359          */
 360         return pcmk_ok;
 361     }
 362 
 363     params = alert_key2param(params, PCMK__alert_key_node, node);
 364     params = alert_key2param(params, PCMK__alert_key_rsc, op->rsc_id);
 365     params = alert_key2param(params, PCMK__alert_key_task, op->op_type);
 366     params = alert_key2param_ms(params, PCMK__alert_key_interval,
 367                                 op->interval_ms);
 368     params = alert_key2param_int(params, PCMK__alert_key_target_rc, target_rc);
 369     params = alert_key2param_int(params, PCMK__alert_key_status, op->op_status);
 370     params = alert_key2param_int(params, PCMK__alert_key_rc, op->rc);
 371 
 372     /* Reoccurring operations do not set exec_time, so on timeout, set it
 373      * to the operation timeout since that's closer to the actual value.
 374      */
 375     if ((op->op_status == PCMK_EXEC_TIMEOUT) && (op->exec_time == 0)) {
 376         params = alert_key2param_int(params, PCMK__alert_key_exec_time,
 377                                      op->timeout);
 378     } else {
 379         params = alert_key2param_int(params, PCMK__alert_key_exec_time,
 380                                      op->exec_time);
 381     }
 382 
 383     if (op->op_status == PCMK_EXEC_DONE) {
 384         params = alert_key2param(params, PCMK__alert_key_desc,
 385                                  crm_exit_str((crm_exit_t) op->rc));
 386     } else {
 387         params = alert_key2param(params, PCMK__alert_key_desc,
 388                                  pcmk_exec_status_str(op->op_status));
 389     }
 390 
 391     rc = exec_alert_list(lrmd, alert_list, pcmk__alert_resource, NULL, params);
 392     lrmd_key_value_freeall(params);
 393     return rc;
 394 }

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