diff --git a/include/crm/common/alerts_internal.h b/include/crm/common/alerts_internal.h index ef64fab1d6..03619f53d6 100644 --- a/include/crm/common/alerts_internal.h +++ b/include/crm/common/alerts_internal.h @@ -1,92 +1,92 @@ /* * Copyright 2015-2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PCMK__ALERT_INTERNAL__H #define PCMK__ALERT_INTERNAL__H #include #include /* Default-Timeout to use before killing a alerts script (in milliseconds) */ # define PCMK__ALERT_DEFAULT_TIMEOUT_MS (30000) /* Default-Format-String used to pass timestamps to the alerts scripts */ # define PCMK__ALERT_DEFAULT_TSTAMP_FORMAT "%H:%M:%S.%06N" enum pcmk__alert_flags { pcmk__alert_none = 0, pcmk__alert_node = (1 << 0), pcmk__alert_fencing = (1 << 1), pcmk__alert_resource = (1 << 2), pcmk__alert_attribute = (1 << 3), pcmk__alert_default = pcmk__alert_node|pcmk__alert_fencing| pcmk__alert_resource, }; typedef struct { char *id; char *path; char *tstamp_format; char *recipient; char **select_attribute_name; GHashTable *envvars; int timeout; uint32_t flags; } pcmk__alert_t; enum pcmk__alert_keys_e { PCMK__alert_key_recipient = 0, PCMK__alert_key_node, PCMK__alert_key_nodeid, PCMK__alert_key_rsc, PCMK__alert_key_task, PCMK__alert_key_interval, PCMK__alert_key_desc, PCMK__alert_key_status, PCMK__alert_key_target_rc, PCMK__alert_key_rc, PCMK__alert_key_kind, PCMK__alert_key_version, PCMK__alert_key_node_sequence, PCMK__alert_key_timestamp, PCMK__alert_key_attribute_name, PCMK__alert_key_attribute_value, PCMK__alert_key_timestamp_epoch, PCMK__alert_key_timestamp_usec, PCMK__alert_key_exec_time, PCMK__alert_key_select_kind, PCMK__alert_key_select_attribute_name }; #define PCMK__ALERT_INTERNAL_KEY_MAX 19 #define PCMK__ALERT_NODE_SEQUENCE "CRM_alert_node_sequence" extern const char *pcmk__alert_keys[PCMK__ALERT_INTERNAL_KEY_MAX][3]; pcmk__alert_t *pcmk__dup_alert(const pcmk__alert_t *entry); pcmk__alert_t *pcmk__alert_new(const char *id, const char *path); void pcmk__free_alert(pcmk__alert_t *entry); void pcmk__add_alert_key(GHashTable *table, enum pcmk__alert_keys_e name, const char *value); void pcmk__add_alert_key_int(GHashTable *table, enum pcmk__alert_keys_e name, int value); -bool pcmk__alert_in_patchset(xmlNode *msg, bool config); +bool pcmk__alert_in_patchset(const xmlNode *msg, bool config); static inline const char * pcmk__alert_flag2text(enum pcmk__alert_flags flag) { switch (flag) { case pcmk__alert_node: return "node"; case pcmk__alert_fencing: return "fencing"; case pcmk__alert_resource: return "resource"; case pcmk__alert_attribute: return "attribute"; default: return "unknown"; } } #endif diff --git a/lib/common/alerts.c b/lib/common/alerts.c index b8e46dba28..32eb6dba40 100644 --- a/lib/common/alerts.c +++ b/lib/common/alerts.c @@ -1,251 +1,277 @@ /* * Copyright 2015-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include +#include #include #include /* for F_CIB_UPDATE_RESULT */ /* * to allow script compatibility we can have more than one * set of environment variables */ const char *pcmk__alert_keys[PCMK__ALERT_INTERNAL_KEY_MAX][3] = { [PCMK__alert_key_recipient] = { "CRM_notify_recipient", "CRM_alert_recipient", NULL }, [PCMK__alert_key_node] = { "CRM_notify_node", "CRM_alert_node", NULL }, [PCMK__alert_key_nodeid] = { "CRM_notify_nodeid", "CRM_alert_nodeid", NULL }, [PCMK__alert_key_rsc] = { "CRM_notify_rsc", "CRM_alert_rsc", NULL }, [PCMK__alert_key_task] = { "CRM_notify_task", "CRM_alert_task", NULL }, [PCMK__alert_key_interval] = { "CRM_notify_interval", "CRM_alert_interval", NULL }, [PCMK__alert_key_desc] = { "CRM_notify_desc", "CRM_alert_desc", NULL }, [PCMK__alert_key_status] = { "CRM_notify_status", "CRM_alert_status", NULL }, [PCMK__alert_key_target_rc] = { "CRM_notify_target_rc", "CRM_alert_target_rc", NULL }, [PCMK__alert_key_rc] = { "CRM_notify_rc", "CRM_alert_rc", NULL }, [PCMK__alert_key_kind] = { "CRM_notify_kind", "CRM_alert_kind", NULL }, [PCMK__alert_key_version] = { "CRM_notify_version", "CRM_alert_version", NULL }, [PCMK__alert_key_node_sequence] = { "CRM_notify_node_sequence", PCMK__ALERT_NODE_SEQUENCE, NULL }, [PCMK__alert_key_timestamp] = { "CRM_notify_timestamp", "CRM_alert_timestamp", NULL }, [PCMK__alert_key_attribute_name] = { "CRM_notify_attribute_name", "CRM_alert_attribute_name", NULL }, [PCMK__alert_key_attribute_value] = { "CRM_notify_attribute_value", "CRM_alert_attribute_value", NULL }, [PCMK__alert_key_timestamp_epoch] = { "CRM_notify_timestamp_epoch", "CRM_alert_timestamp_epoch", NULL }, [PCMK__alert_key_timestamp_usec] = { "CRM_notify_timestamp_usec", "CRM_alert_timestamp_usec", NULL }, [PCMK__alert_key_exec_time] = { "CRM_notify_exec_time", "CRM_alert_exec_time", NULL } }; /*! * \brief Create a new alert entry structure * * \param[in] id ID to use * \param[in] path Path to alert agent executable * * \return Pointer to newly allocated alert entry * \note Non-string fields will be filled in with defaults. * It is the caller's responsibility to free the result, * using pcmk__free_alert(). */ pcmk__alert_t * pcmk__alert_new(const char *id, const char *path) { pcmk__alert_t *entry = calloc(1, sizeof(pcmk__alert_t)); CRM_ASSERT(entry && id && path); entry->id = strdup(id); entry->path = strdup(path); entry->timeout = PCMK__ALERT_DEFAULT_TIMEOUT_MS; entry->flags = pcmk__alert_default; return entry; } void pcmk__free_alert(pcmk__alert_t *entry) { if (entry) { free(entry->id); free(entry->path); free(entry->tstamp_format); free(entry->recipient); g_strfreev(entry->select_attribute_name); if (entry->envvars) { g_hash_table_destroy(entry->envvars); } free(entry); } } /*! * \internal * \brief Duplicate an alert entry * * \param[in] entry Alert entry to duplicate * * \return Duplicate of alert entry */ pcmk__alert_t * pcmk__dup_alert(const pcmk__alert_t *entry) { pcmk__alert_t *new_entry = pcmk__alert_new(entry->id, entry->path); new_entry->timeout = entry->timeout; new_entry->flags = entry->flags; new_entry->envvars = pcmk__str_table_dup(entry->envvars); pcmk__str_update(&new_entry->tstamp_format, entry->tstamp_format); pcmk__str_update(&new_entry->recipient, entry->recipient); if (entry->select_attribute_name) { new_entry->select_attribute_name = g_strdupv(entry->select_attribute_name); } return new_entry; } void pcmk__add_alert_key(GHashTable *table, enum pcmk__alert_keys_e name, const char *value) { for (const char **key = pcmk__alert_keys[name]; *key; key++) { crm_trace("Inserting alert key %s = '%s'", *key, value); if (value) { g_hash_table_insert(table, strdup(*key), strdup(value)); } else { g_hash_table_remove(table, *key); } } } void pcmk__add_alert_key_int(GHashTable *table, enum pcmk__alert_keys_e name, int value) { for (const char **key = pcmk__alert_keys[name]; *key; key++) { crm_trace("Inserting alert key %s = %d", *key, value); g_hash_table_insert(table, strdup(*key), pcmk__itoa(value)); } } -#define XPATH_PATCHSET1_DIFF "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED +#define XPATH_DIFF_V1 "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED +#define XPATH_CRMCONFIG_V1 XPATH_DIFF_V1 "//" XML_CIB_TAG_CRMCONFIG +#define XPATH_ALERTS_V1 XPATH_DIFF_V1 "//" XML_CIB_TAG_ALERTS +#define XPATH_EITHER_V1 XPATH_CRMCONFIG_V1 "|" XPATH_ALERTS_V1 -#define XPATH_PATCHSET1_CRMCONFIG XPATH_PATCHSET1_DIFF "//" XML_CIB_TAG_CRMCONFIG -#define XPATH_PATCHSET1_ALERTS XPATH_PATCHSET1_DIFF "//" XML_CIB_TAG_ALERTS +/*! + * \internal + * \brief Check whether a CIB update affects alerts (using v1 diff) + * + * \param[in] patchset XML containing CIB update + * \param[in] config Whether to check for crmconfig change as well + * + * \return \c true if the update affects alerts, or \c false otherwise + */ +static bool +alert_in_patchset_v1(const xmlNode *patchset, bool config) +{ + const char *xpath = config? XPATH_EITHER_V1 : XPATH_ALERTS_V1; + xmlXPathObject *xpath_obj = xpath_search(patchset, xpath); -#define XPATH_PATCHSET1_EITHER \ - XPATH_PATCHSET1_CRMCONFIG " | " XPATH_PATCHSET1_ALERTS + if (xpath_obj != NULL) { + freeXpathObject(xpath_obj); + return true; + } + return false; +} -#define XPATH_CONFIG "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION +/*! + * \internal + * \brief Check whether a CIB update affects alerts (using v2 diff) + * + * \param[in] patchset XML containing CIB update + * \param[in] config Whether to check for crmconfig change as well + * + * \return \c true if the update affects alerts, or \c false otherwise + */ +static bool +alert_in_patchset_v2(const xmlNode *patchset, bool config) +{ + const char *xpath_alerts = pcmk__cib_abs_xpath_for(XML_CIB_TAG_ALERTS); + const char *xpath_parent = pcmk_cib_parent_name_for(XML_CIB_TAG_ALERTS); + const char *xpath_crmconfig = + pcmk__cib_abs_xpath_for(XML_CIB_TAG_CRMCONFIG); + + for (const xmlNode *change = first_named_child(patchset, XML_DIFF_CHANGE); + change != NULL; change = crm_next_same_xml(change)) { -#define XPATH_CRMCONFIG XPATH_CONFIG "/" XML_CIB_TAG_CRMCONFIG "/" -#define XPATH_ALERTS XPATH_CONFIG "/" XML_CIB_TAG_ALERTS + const char *xpath = crm_element_value(change, XML_DIFF_PATH); + + if (pcmk__starts_with(xpath, xpath_alerts) + || (config && pcmk__starts_with(xpath, xpath_crmconfig))) { + + // Change to an existing alerts or crm_config section + return true; + } + + if (pcmk__str_eq(xpath, xpath_parent, pcmk__str_none) + && pcmk__xe_is(pcmk__xml_first_child(change), XML_CIB_TAG_ALERTS)) { + + // Newly added alerts section in a create operation + return true; + } + } + return false; +} /*! * \internal * \brief Check whether a CIB update affects alerts * * \param[in] msg XML containing CIB update * \param[in] config Whether to check for crmconfig change as well * - * \return TRUE if update affects alerts, FALSE otherwise + * \return \c true if the update affects alerts, or \c false otherwise */ bool -pcmk__alert_in_patchset(xmlNode *msg, bool config) +pcmk__alert_in_patchset(const xmlNode *msg, bool config) { - int rc = -1; - int format= 1; - xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT); - xmlNode *change = NULL; - xmlXPathObject *xpathObj = NULL; + int rc = pcmk_err_generic; + int format = 1; + xmlNode *patchset = NULL; - CRM_CHECK(msg != NULL, return FALSE); + CRM_CHECK(msg != NULL, return false); - crm_element_value_int(msg, F_CIB_RC, &rc); - if (rc < pcmk_ok) { + if ((crm_element_value_int(msg, F_CIB_RC, &rc) != 0) || (rc != pcmk_ok)) { crm_trace("Ignore failed CIB update: %s (%d)", pcmk_strerror(rc), rc); - return FALSE; + return false; } - crm_element_value_int(patchset, "format", &format); - if (format == 1) { - const char *diff = (config? XPATH_PATCHSET1_EITHER : XPATH_PATCHSET1_ALERTS); - - if ((xpathObj = xpath_search(msg, diff)) != NULL) { - freeXpathObject(xpathObj); - return TRUE; - } - } else if (format == 2) { - for (change = pcmk__xml_first_child(patchset); change != NULL; - change = pcmk__xml_next(change)) { - const char *xpath = crm_element_value(change, XML_DIFF_PATH); - - if (xpath == NULL) { - continue; - } - - if ((!config || !strstr(xpath, XPATH_CRMCONFIG)) - && !strstr(xpath, XPATH_ALERTS)) { - - /* this is not a change to an existing section ... */ + patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT); + CRM_CHECK(patchset != NULL, return false); - xmlNode *section = NULL; - - if ((strcmp(xpath, XPATH_CONFIG) != 0) || - ((section = pcmk__xml_first_child(change)) == NULL) || - !pcmk__xe_is(section, XML_CIB_TAG_ALERTS)) { - - /* ... nor is it a newly added alerts section */ - continue; - } - } + crm_element_value_int(patchset, "format", &format); + switch (format) { + case 1: + return alert_in_patchset_v1(patchset, config); - return TRUE; - } + case 2: + return alert_in_patchset_v2(patchset, config); - } else { - crm_warn("Unknown patch format: %d", format); + default: + crm_warn("Unknown patch format: %d", format); + return false; } - return FALSE; }