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

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