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

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