root/lib/pengine/rules_alerts.c

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

DEFINITIONS

This source file includes following definitions.
  1. get_meta_attrs_from_cib
  2. get_envvars_from_cib
  3. unpack_alert_filter
  4. unpack_alert
  5. pe_unpack_alerts
  6. pe_free_alert_list

   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 #include <crm/crm.h>
  12 #include <crm/common/xml.h>
  13 #include <crm/pengine/rules.h>
  14 #include <crm/common/alerts_internal.h>
  15 #include <crm/common/xml_internal.h>
  16 #include <crm/pengine/rules_internal.h>
  17 
  18 /*!
  19  * \internal
  20  * \brief Unpack an alert's or alert recipient's meta attributes
  21  *
  22  * \param[in,out] basenode     Alert or recipient XML
  23  * \param[in,out] entry        Where to store unpacked values
  24  * \param[in,out] max_timeout  Max timeout of all alerts and recipients thus far
  25  *
  26  * \return Standard Pacemaker return code
  27  */
  28 static int
  29 get_meta_attrs_from_cib(xmlNode *basenode, pcmk__alert_t *entry,
     /* [previous][next][first][last][top][bottom][index][help] */
  30                         guint *max_timeout)
  31 {
  32     GHashTable *config_hash = pcmk__strkey_table(free, free);
  33     crm_time_t *now = crm_time_new(NULL);
  34     const char *value = NULL;
  35     int rc = pcmk_rc_ok;
  36 
  37     pe_unpack_nvpairs(basenode, basenode, PCMK_XE_META_ATTRIBUTES, NULL,
  38                       config_hash, NULL, FALSE, now, NULL);
  39     crm_time_free(now);
  40 
  41     value = g_hash_table_lookup(config_hash, PCMK_META_ENABLED);
  42     if ((value != NULL) && !crm_is_true(value)) {
  43         // No need to continue unpacking
  44         rc = pcmk_rc_disabled;
  45         goto done;
  46     }
  47 
  48     value = g_hash_table_lookup(config_hash, PCMK_META_TIMEOUT);
  49     if (value) {
  50         long long timeout_ms = crm_get_msec(value);
  51 
  52         entry->timeout = (int) QB_MIN(timeout_ms, INT_MAX);
  53         if (entry->timeout <= 0) {
  54             if (entry->timeout == 0) {
  55                 crm_trace("Alert %s uses default timeout of %dmsec",
  56                           entry->id, PCMK__ALERT_DEFAULT_TIMEOUT_MS);
  57             } else {
  58                 pcmk__config_warn("Alert %s has invalid timeout value '%s', "
  59                                   "using default (%d ms)",
  60                                   entry->id, value,
  61                                   PCMK__ALERT_DEFAULT_TIMEOUT_MS);
  62             }
  63             entry->timeout = PCMK__ALERT_DEFAULT_TIMEOUT_MS;
  64         } else {
  65             crm_trace("Alert %s uses timeout of %dmsec",
  66                       entry->id, entry->timeout);
  67         }
  68         if (entry->timeout > *max_timeout) {
  69             *max_timeout = entry->timeout;
  70         }
  71     }
  72     value = g_hash_table_lookup(config_hash, PCMK_META_TIMESTAMP_FORMAT);
  73     if (value) {
  74         /* hard to do any checks here as merely anything can
  75          * can be a valid time-format-string
  76          */
  77         entry->tstamp_format = strdup(value);
  78         crm_trace("Alert %s uses timestamp format '%s'",
  79                   entry->id, entry->tstamp_format);
  80     }
  81 
  82 done:
  83     g_hash_table_destroy(config_hash);
  84     return rc;
  85 }
  86 
  87 static void
  88 get_envvars_from_cib(xmlNode *basenode, pcmk__alert_t *entry)
     /* [previous][next][first][last][top][bottom][index][help] */
  89 {
  90     xmlNode *child;
  91 
  92     if ((basenode == NULL) || (entry == NULL)) {
  93         return;
  94     }
  95 
  96     child = pcmk__xe_first_child(basenode, PCMK_XE_INSTANCE_ATTRIBUTES, NULL,
  97                                  NULL);
  98     if (child == NULL) {
  99         return;
 100     }
 101 
 102     if (entry->envvars == NULL) {
 103         entry->envvars = pcmk__strkey_table(free, free);
 104     }
 105 
 106     for (child = pcmk__xe_first_child(child, PCMK_XE_NVPAIR, NULL, NULL);
 107          child != NULL; child = pcmk__xe_next_same(child)) {
 108 
 109         const char *name = crm_element_value(child, PCMK_XA_NAME);
 110         const char *value = crm_element_value(child, PCMK_XA_VALUE);
 111 
 112         if (value == NULL) {
 113             value = "";
 114         }
 115         pcmk__insert_dup(entry->envvars, name, value);
 116         crm_trace("Alert %s: added environment variable %s='%s'",
 117                   entry->id, name, value);
 118     }
 119 }
 120 
 121 static void
 122 unpack_alert_filter(xmlNode *basenode, pcmk__alert_t *entry)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     xmlNode *select = pcmk__xe_first_child(basenode, PCMK_XE_SELECT, NULL,
 125                                            NULL);
 126     xmlNode *event_type = NULL;
 127     uint32_t flags = pcmk__alert_none;
 128 
 129     for (event_type = pcmk__xe_first_child(select, NULL, NULL, NULL);
 130          event_type != NULL; event_type = pcmk__xe_next(event_type)) {
 131 
 132         if (pcmk__xe_is(event_type, PCMK_XE_SELECT_FENCING)) {
 133             flags |= pcmk__alert_fencing;
 134 
 135         } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_NODES)) {
 136             flags |= pcmk__alert_node;
 137 
 138         } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_RESOURCES)) {
 139             flags |= pcmk__alert_resource;
 140 
 141         } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_ATTRIBUTES)) {
 142             xmlNode *attr;
 143             const char *attr_name;
 144             int nattrs = 0;
 145 
 146             flags |= pcmk__alert_attribute;
 147             for (attr = pcmk__xe_first_child(event_type, PCMK_XE_ATTRIBUTE,
 148                                              NULL, NULL);
 149                  attr != NULL; attr = pcmk__xe_next_same(attr)) {
 150 
 151                 attr_name = crm_element_value(attr, PCMK_XA_NAME);
 152                 if (attr_name) {
 153                     if (nattrs == 0) {
 154                         g_strfreev(entry->select_attribute_name);
 155                         entry->select_attribute_name = NULL;
 156                     }
 157                     ++nattrs;
 158                     entry->select_attribute_name = pcmk__realloc(entry->select_attribute_name,
 159                                                                  (nattrs + 1) * sizeof(char*));
 160                     entry->select_attribute_name[nattrs - 1] = strdup(attr_name);
 161                     entry->select_attribute_name[nattrs] = NULL;
 162                 }
 163             }
 164         }
 165     }
 166 
 167     if (flags != pcmk__alert_none) {
 168         entry->flags = flags;
 169         crm_debug("Alert %s receives events: attributes:%s%s%s%s",
 170                   entry->id,
 171                   (pcmk_is_set(flags, pcmk__alert_attribute)?
 172                    (entry->select_attribute_name? "some" : "all") : "none"),
 173                   (pcmk_is_set(flags, pcmk__alert_fencing)? " fencing" : ""),
 174                   (pcmk_is_set(flags, pcmk__alert_node)? " nodes" : ""),
 175                   (pcmk_is_set(flags, pcmk__alert_resource)? " resources" : ""));
 176     }
 177 }
 178 
 179 /*!
 180  * \internal
 181  * \brief Unpack an alert or an alert recipient
 182  *
 183  * \param[in,out] alert        Alert or recipient XML
 184  * \param[in,out] entry        Where to store unpacked values
 185  * \param[in,out] max_timeout  Max timeout of all alerts and recipients thus far
 186  *
 187  * \return Standard Pacemaker return code
 188  */
 189 static int
 190 unpack_alert(xmlNode *alert, pcmk__alert_t *entry, guint *max_timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
 191 {
 192     int rc = pcmk_rc_ok;
 193 
 194     get_envvars_from_cib(alert, entry);
 195     rc = get_meta_attrs_from_cib(alert, entry, max_timeout);
 196     if (rc == pcmk_rc_ok) {
 197         unpack_alert_filter(alert, entry);
 198     }
 199     return rc;
 200 }
 201 
 202 /*!
 203  * \internal
 204  * \brief Unpack a CIB alerts section
 205  *
 206  * \param[in] alerts  XML of alerts section
 207  *
 208  * \return  List of unpacked alert entries
 209  *
 210  * \note Unlike most unpack functions, this is not used by the scheduler itself,
 211  *       but is supplied for use by daemons that need to send alerts.
 212  */
 213 GList *
 214 pe_unpack_alerts(const xmlNode *alerts)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216     xmlNode *alert;
 217     pcmk__alert_t *entry;
 218     guint max_timeout = 0;
 219     GList *alert_list = NULL;
 220 
 221     if (alerts == NULL) {
 222         return alert_list;
 223     }
 224 
 225     for (alert = pcmk__xe_first_child(alerts, PCMK_XE_ALERT, NULL, NULL);
 226          alert != NULL; alert = pcmk__xe_next_same(alert)) {
 227 
 228         xmlNode *recipient;
 229         int recipients = 0;
 230         const char *alert_id = pcmk__xe_id(alert);
 231         const char *alert_path = crm_element_value(alert, PCMK_XA_PATH);
 232 
 233         /* The schema should enforce this, but to be safe ... */
 234         if (alert_id == NULL) {
 235             pcmk__config_warn("Ignoring invalid alert without " PCMK_XA_ID);
 236             crm_log_xml_info(alert, "missing-id");
 237             continue;
 238         }
 239         if (alert_path == NULL) {
 240             pcmk__config_warn("Ignoring alert %s: No " PCMK_XA_PATH, alert_id);
 241             continue;
 242         }
 243 
 244         entry = pcmk__alert_new(alert_id, alert_path);
 245 
 246         if (unpack_alert(alert, entry, &max_timeout) != pcmk_rc_ok) {
 247             // Don't allow recipients to override if entire alert is disabled
 248             crm_debug("Alert %s is disabled", entry->id);
 249             pcmk__free_alert(entry);
 250             continue;
 251         }
 252 
 253         if (entry->tstamp_format == NULL) {
 254             entry->tstamp_format = strdup(PCMK__ALERT_DEFAULT_TSTAMP_FORMAT);
 255         }
 256 
 257         crm_debug("Alert %s: path=%s timeout=%dms tstamp-format='%s' %u vars",
 258                   entry->id, entry->path, entry->timeout, entry->tstamp_format,
 259                   (entry->envvars? g_hash_table_size(entry->envvars) : 0));
 260 
 261         for (recipient = pcmk__xe_first_child(alert, PCMK_XE_RECIPIENT, NULL,
 262                                               NULL);
 263              recipient != NULL; recipient = pcmk__xe_next_same(recipient)) {
 264 
 265             pcmk__alert_t *recipient_entry = pcmk__dup_alert(entry);
 266 
 267             recipients++;
 268             recipient_entry->recipient = crm_element_value_copy(recipient,
 269                                                                 PCMK_XA_VALUE);
 270 
 271             if (unpack_alert(recipient, recipient_entry,
 272                              &max_timeout) != pcmk_rc_ok) {
 273                 crm_debug("Alert %s: recipient %s is disabled",
 274                           entry->id, recipient_entry->id);
 275                 pcmk__free_alert(recipient_entry);
 276                 continue;
 277             }
 278             alert_list = g_list_prepend(alert_list, recipient_entry);
 279             crm_debug("Alert %s has recipient %s with value %s and %d envvars",
 280                       entry->id, pcmk__xe_id(recipient),
 281                       recipient_entry->recipient,
 282                       (recipient_entry->envvars?
 283                        g_hash_table_size(recipient_entry->envvars) : 0));
 284         }
 285 
 286         if (recipients == 0) {
 287             alert_list = g_list_prepend(alert_list, entry);
 288         } else {
 289             pcmk__free_alert(entry);
 290         }
 291     }
 292     return alert_list;
 293 }
 294 
 295 /*!
 296  * \internal
 297  * \brief Free an alert list generated by pe_unpack_alerts()
 298  *
 299  * \param[in,out] alert_list  Alert list to free
 300  */
 301 void
 302 pe_free_alert_list(GList *alert_list)
     /* [previous][next][first][last][top][bottom][index][help] */
 303 {
 304     if (alert_list) {
 305         g_list_free_full(alert_list, (GDestroyNotify) pcmk__free_alert);
 306     }
 307 }

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