diff --git a/extra/alerts/alert_snmp.sh.sample b/extra/alerts/alert_snmp.sh.sample
index 0a8be78566..5f99157836 100644
--- a/extra/alerts/alert_snmp.sh.sample
+++ b/extra/alerts/alert_snmp.sh.sample
@@ -1,195 +1,200 @@
#!/bin/sh
#
# Description: Manages a SNMP trap, provided by NTT OSSC as a
# script under Pacemaker control
#
# Copyright (c) 2016 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
##############################################################################
# This sample script assumes that only users who already have
# hacluster-equivalent access to the cluster nodes can edit the CIB. Otherwise,
# a malicious user could run commands as hacluster by inserting shell code into
# the trap_options or timestamp-format parameters.
#
# Sample configuration (cib fragment in xml notation)
# ================================
#
#
#
#
#
#
#
#
#
#
#
#
#
# ================================
#
# This uses the official Pacemaker MIB.
# 1.3.6.1.4.1.32723 has been assigned to the project by IANA:
# http://www.iana.org/assignments/enterprise-numbers
#
# The original usage (by crm_mon) was:
# define PACEMAKER_PREFIX "1.3.6.1.4.1.32723"
# define PACEMAKER_TRAP_PREFIX PACEMAKER_PREFIX ".1"
# define snmp_crm_trap_oid PACEMAKER_TRAP_PREFIX
# define snmp_crm_oid_node PACEMAKER_TRAP_PREFIX ".1"
# define snmp_crm_oid_rsc PACEMAKER_TRAP_PREFIX ".2"
# define snmp_crm_oid_task PACEMAKER_TRAP_PREFIX ".3"
# define snmp_crm_oid_desc PACEMAKER_TRAP_PREFIX ".4"
# define snmp_crm_oid_status PACEMAKER_TRAP_PREFIX ".5"
# define snmp_crm_oid_rc PACEMAKER_TRAP_PREFIX ".6"
# define snmp_crm_oid_trc PACEMAKER_TRAP_PREFIX ".7"
if [ -z "$CRM_alert_version" ]; then
echo "$0 must be run by Pacemaker version 1.1.15 or later"
exit 0
fi
if [ -z "$CRM_alert_recipient" ]; then
echo "$0 requires a recipient configured with the SNMP server IP address"
exit 0
fi
#
trap_binary_default="/usr/bin/snmptrap"
trap_version_default="2c"
trap_options_default=""
trap_community_default="public"
trap_node_states_default="all"
trap_fencing_tasks_default="all"
trap_resource_tasks_default="all"
trap_monitor_success_default="false"
trap_add_hires_timestamp_oid_default="true"
trap_snmp_persistent_dir_default="/var/lib/pacemaker/snmp"
: ${trap_binary=${trap_binary_default}}
: ${trap_version=${trap_version_default}}
: ${trap_options=${trap_options_default}}
: ${trap_community=${trap_community_default}}
: ${trap_node_states=${trap_node_states_default}}
: ${trap_fencing_tasks=${trap_fencing_tasks_default}}
: ${trap_resource_tasks=${trap_resource_tasks_default}}
: ${trap_monitor_success=${trap_monitor_success_default}}
: ${trap_add_hires_timestamp_oid=${trap_add_hires_timestamp_oid_default}}
: ${trap_snmp_persistent_dir=${trap_snmp_persistent_dir_default}}
if [ "${trap_add_hires_timestamp_oid}" = "true" ]; then
- hires_timestamp="HOST-RESOURCES-MIB::hrSystemDate s ${CRM_alert_timestamp}"
+#
+# Please make hires_timestamp with CRM_alert_timestamp_epoch and CRM_alert_timestamp_usec according to Netsnmp's DateAndTime specification.
+# The current format is "%Y-%m-%d,%H:%M:%S.%01N".
+#
+ trap_fmt_timestamp=`date --date="@${CRM_alert_timestamp_epoch}" +"%Y-%m-%d,%H:%M:%S."`$(echo ${CRM_alert_timestamp_usec} | cut -b 1)
+ hires_timestamp="HOST-RESOURCES-MIB::hrSystemDate s ${trap_fmt_timestamp}"
fi
is_in_list() {
item_list=`echo "$1" | tr ',' ' '`
if [ "${item_list}" = "all" ]; then
return 0
else
for act in $item_list
do
act=`echo "$act" | tr A-Z a-z`
[ "$act" != "$2" ] && continue
return 0
done
fi
return 1
}
if [ -z ${SNMP_PERSISTENT_DIR} ]; then
export SNMP_PERSISTENT_DIR="${trap_snmp_persistent_dir}"
# mkdir for snmp trap tools.
if [ ! -d ${SNMP_PERSISTENT_DIR} ]; then
mkdir -p ${SNMP_PERSISTENT_DIR}
fi
fi
rc=0
case "$CRM_alert_kind" in
node)
is_in_list "${trap_node_states}" "${CRM_alert_desc}"
[ $? -ne 0 ] && exit 0
output=`"${trap_binary}" -v "${trap_version}" ${trap_options} \
-c "${trap_community}" "${CRM_alert_recipient}" "" \
PACEMAKER-MIB::pacemakerNotificationTrap \
PACEMAKER-MIB::pacemakerNotificationNode s "${CRM_alert_node}" \
PACEMAKER-MIB::pacemakerNotificationDescription s "${CRM_alert_desc}" \
${hires_timestamp} 2>&1`
rc=$?
;;
fencing)
is_in_list "${trap_fencing_tasks}" "${CRM_alert_task}"
[ $? -ne 0 ] && exit 0
output=`"${trap_binary}" -v "${trap_version}" ${trap_options} \
-c "${trap_community}" "${CRM_alert_recipient}" "" \
PACEMAKER-MIB::pacemakerNotificationTrap \
PACEMAKER-MIB::pacemakerNotificationNode s "${CRM_alert_node}" \
PACEMAKER-MIB::pacemakerNotificationOperation s "${CRM_alert_task}" \
PACEMAKER-MIB::pacemakerNotificationDescription s "${CRM_alert_desc}" \
PACEMAKER-MIB::pacemakerNotificationReturnCode i ${CRM_alert_rc} \
${hires_timestamp} 2>&1`
rc=$?
;;
resource)
is_in_list "${trap_resource_tasks}" "${CRM_alert_task}"
[ $? -ne 0 ] && exit 0
case "${CRM_alert_desc}" in
Cancelled) ;;
*)
if [ "${trap_monitor_success}" = "false" ] \
&& [ "${CRM_alert_rc}" = "${CRM_alert_target_rc}" ] \
&& [ "${CRM_alert_task}" = "monitor" ]; then
exit
fi
output=`"${trap_binary}" -v "${trap_version}" ${trap_options} \
-c "${trap_community}" "${CRM_alert_recipient}" "" \
PACEMAKER-MIB::pacemakerNotificationTrap \
PACEMAKER-MIB::pacemakerNotificationNode s "${CRM_alert_node}" \
PACEMAKER-MIB::pacemakerNotificationResource s "${CRM_alert_rsc}" \
PACEMAKER-MIB::pacemakerNotificationOperation s "${CRM_alert_task}" \
PACEMAKER-MIB::pacemakerNotificationDescription s "${CRM_alert_desc}" \
PACEMAKER-MIB::pacemakerNotificationStatus i ${CRM_alert_status} \
PACEMAKER-MIB::pacemakerNotificationReturnCode i ${CRM_alert_rc} \
PACEMAKER-MIB::pacemakerNotificationTargetReturnCode i ${CRM_alert_target_rc} \
${hires_timestamp} 2>&1`
rc=$?
;;
esac
;;
attribute)
output=`"${trap_binary}" -v "${trap_version}" ${trap_options} \
-c "${trap_community}" "${CRM_alert_recipient}" "" \
PACEMAKER-MIB::pacemakerNotificationTrap \
PACEMAKER-MIB::pacemakerNotificationNode s "${CRM_alert_node}" \
PACEMAKER-MIB::pacemakerNotificationAttributeName s "${CRM_alert_attribute_name}" \
PACEMAKER-MIB::pacemakerNotificationAttributeValue s "${CRM_alert_attribute_value}" \
${hires_timestamp} 2>&1`
rc=$?
;;
*)
;;
esac
if [ $rc -ne 0 ]; then
echo "${trap_binary} returned error : rc=${rc} ${output}"
fi
diff --git a/include/crm/common/alerts_internal.h b/include/crm/common/alerts_internal.h
index 8c325b8a8b..8bfcb8c01d 100644
--- a/include/crm/common/alerts_internal.h
+++ b/include/crm/common/alerts_internal.h
@@ -1,112 +1,114 @@
/*
* Copyright (C) 2015 Andrew Beekhof
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ALERT_INTERNAL_H
#define ALERT_INTERNAL_H
#include
#include
/* Default-Timeout to use before killing a alerts script (in milliseconds) */
# define CRM_ALERT_DEFAULT_TIMEOUT_MS (30000)
/* Default-Format-String used to pass timestamps to the alerts scripts */
# define CRM_ALERT_DEFAULT_TSTAMP_FORMAT "%H:%M:%S.%06N"
typedef struct {
char *name;
char *value;
} crm_alert_envvar_t;
enum crm_alert_flags {
crm_alert_none = 0x0000,
crm_alert_node = 0x0001,
crm_alert_fencing = 0x0002,
crm_alert_resource = 0x0004,
crm_alert_attribute = 0x0008,
crm_alert_default = crm_alert_node|crm_alert_fencing|crm_alert_resource
};
typedef struct {
char *id;
char *path;
char *tstamp_format;
char *recipient;
char **select_attribute_name;
GHashTable *envvars;
int timeout;
uint32_t flags;
} crm_alert_entry_t;
enum crm_alert_keys_e {
CRM_alert_recipient = 0,
CRM_alert_node,
CRM_alert_nodeid,
CRM_alert_rsc,
CRM_alert_task,
CRM_alert_interval,
CRM_alert_desc,
CRM_alert_status,
CRM_alert_target_rc,
CRM_alert_rc,
CRM_alert_kind,
CRM_alert_version,
CRM_alert_node_sequence,
CRM_alert_timestamp,
CRM_alert_attribute_name,
CRM_alert_attribute_value,
+ CRM_alert_timestamp_epoch,
+ CRM_alert_timestamp_usec,
CRM_alert_select_kind,
CRM_alert_select_attribute_name
};
-#define CRM_ALERT_INTERNAL_KEY_MAX 16
+#define CRM_ALERT_INTERNAL_KEY_MAX 18
#define CRM_ALERT_NODE_SEQUENCE "CRM_alert_node_sequence"
extern const char *crm_alert_keys[CRM_ALERT_INTERNAL_KEY_MAX][3];
crm_alert_entry_t *crm_dup_alert_entry(crm_alert_entry_t *entry);
crm_alert_envvar_t *crm_dup_alert_envvar(crm_alert_envvar_t *src);
crm_alert_entry_t *crm_alert_entry_new(const char *id, const char *path);
void crm_free_alert_entry(crm_alert_entry_t *entry);
void crm_free_alert_envvar(crm_alert_envvar_t *entry);
void crm_insert_alert_key(GHashTable *table, enum crm_alert_keys_e name,
const char *value);
void crm_insert_alert_key_int(GHashTable *table, enum crm_alert_keys_e name,
int value);
void crm_unset_alert_keys(void);
void crm_set_envvar_list(crm_alert_entry_t *entry);
void crm_unset_envvar_list(crm_alert_entry_t *entry);
bool crm_patchset_contains_alert(xmlNode *msg, bool config);
static inline const char *
crm_alert_flag2text(enum crm_alert_flags flag)
{
switch (flag) {
case crm_alert_node:
return "node";
case crm_alert_fencing:
return "fencing";
case crm_alert_resource:
return "resource";
case crm_alert_attribute:
return "attribute";
default:
return "unknown";
}
}
#endif
diff --git a/lib/common/alerts.c b/lib/common/alerts.c
index f0e2ca8f67..ed3ca0d043 100644
--- a/lib/common/alerts.c
+++ b/lib/common/alerts.c
@@ -1,290 +1,292 @@
/*
* Copyright (C) 2015 Andrew Beekhof
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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 *crm_alert_keys[CRM_ALERT_INTERNAL_KEY_MAX][3] =
{
[CRM_alert_recipient] = {"CRM_notify_recipient", "CRM_alert_recipient", NULL},
[CRM_alert_node] = {"CRM_notify_node", "CRM_alert_node", NULL},
[CRM_alert_nodeid] = {"CRM_notify_nodeid", "CRM_alert_nodeid", NULL},
[CRM_alert_rsc] = {"CRM_notify_rsc", "CRM_alert_rsc", NULL},
[CRM_alert_task] = {"CRM_notify_task", "CRM_alert_task", NULL},
[CRM_alert_interval] = {"CRM_notify_interval", "CRM_alert_interval", NULL},
[CRM_alert_desc] = {"CRM_notify_desc", "CRM_alert_desc", NULL},
[CRM_alert_status] = {"CRM_notify_status", "CRM_alert_status", NULL},
[CRM_alert_target_rc] = {"CRM_notify_target_rc", "CRM_alert_target_rc", NULL},
[CRM_alert_rc] = {"CRM_notify_rc", "CRM_alert_rc", NULL},
[CRM_alert_kind] = {"CRM_notify_kind", "CRM_alert_kind", NULL},
[CRM_alert_version] = {"CRM_notify_version", "CRM_alert_version", NULL},
[CRM_alert_node_sequence] = {"CRM_notify_node_sequence", CRM_ALERT_NODE_SEQUENCE, NULL},
[CRM_alert_timestamp] = {"CRM_notify_timestamp", "CRM_alert_timestamp", NULL},
[CRM_alert_attribute_name] = {"CRM_notify_attribute_name", "CRM_alert_attribute_name", NULL},
- [CRM_alert_attribute_value] = {"CRM_notify_attribute_value", "CRM_alert_attribute_value", NULL}
+ [CRM_alert_attribute_value] = {"CRM_notify_attribute_value", "CRM_alert_attribute_value", NULL},
+ [CRM_alert_timestamp_epoch] = {"CRM_notify_timestamp_epoch", "CRM_alert_timestamp_epoch", NULL},
+ [CRM_alert_timestamp_usec] = {"CRM_notify_timestamp_usec", "CRM_alert_timestamp_usec", NULL}
};
void
crm_free_alert_envvar(crm_alert_envvar_t *entry)
{
free(entry->name);
free(entry->value);
free(entry);
}
/*!
* \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 crm_free_alert_entry().
*/
crm_alert_entry_t *
crm_alert_entry_new(const char *id, const char *path)
{
crm_alert_entry_t *entry = calloc(1, sizeof(crm_alert_entry_t));
CRM_ASSERT(entry && id && path);
entry->id = strdup(id);
entry->path = strdup(path);
entry->timeout = CRM_ALERT_DEFAULT_TIMEOUT_MS;
entry->flags = crm_alert_default;
return entry;
}
void
crm_free_alert_entry(crm_alert_entry_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);
}
}
crm_alert_envvar_t *
crm_dup_alert_envvar(crm_alert_envvar_t *src)
{
crm_alert_envvar_t *dst = calloc(1, sizeof(crm_alert_envvar_t));
CRM_ASSERT(dst);
dst->name = strdup(src->name);
dst->value = src->value?strdup(src->value):NULL;
return dst;
}
/*!
* \internal
* \brief Duplicate an alert entry
*
* \param[in] entry Alert entry to duplicate
*
* \return Duplicate of alert entry
*/
crm_alert_entry_t *
crm_dup_alert_entry(crm_alert_entry_t *entry)
{
crm_alert_entry_t *new_entry = crm_alert_entry_new(entry->id, entry->path);
new_entry->timeout = entry->timeout;
new_entry->flags = entry->flags;
new_entry->envvars = crm_str_table_dup(entry->envvars);
if (entry->tstamp_format) {
new_entry->tstamp_format = strdup(entry->tstamp_format);
}
if (entry->recipient) {
new_entry->recipient = strdup(entry->recipient);
}
if (entry->select_attribute_name) {
new_entry->select_attribute_name = g_strdupv(entry->select_attribute_name);
}
return new_entry;
}
void
crm_unset_alert_keys()
{
const char **key;
enum crm_alert_keys_e name;
for(name = 0; name < DIMOF(crm_alert_keys); name++) {
for(key = crm_alert_keys[name]; *key; key++) {
crm_trace("Unsetting alert key %s", *key);
unsetenv(*key);
}
}
}
void
crm_insert_alert_key(GHashTable *table, enum crm_alert_keys_e name,
const char *value)
{
for (const char **key = crm_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
crm_insert_alert_key_int(GHashTable *table, enum crm_alert_keys_e name,
int value)
{
for (const char **key = crm_alert_keys[name]; *key; key++) {
crm_trace("Inserting alert key %s = %d", *key, value);
g_hash_table_insert(table, strdup(*key), crm_itoa(value));
}
}
static void
set_envvar(gpointer key, gpointer value, gpointer user_data)
{
gboolean always_unset = GPOINTER_TO_INT(user_data);
crm_trace("%s environment variable %s='%s'",
(value? "Setting" : "Unsetting"),
(char*)key, (value? (char*)value : ""));
if (value && !always_unset) {
setenv(key, value, 1);
} else {
unsetenv(key);
}
}
void
crm_set_envvar_list(crm_alert_entry_t *entry)
{
if (entry->envvars) {
g_hash_table_foreach(entry->envvars, set_envvar, GINT_TO_POINTER(FALSE));
}
}
/*
* \note We have no way of restoring a previous value if one was set.
*/
void
crm_unset_envvar_list(crm_alert_entry_t *entry)
{
if (entry->envvars) {
g_hash_table_foreach(entry->envvars, set_envvar, GINT_TO_POINTER(TRUE));
}
}
#define XPATH_PATCHSET1_DIFF "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
#define XPATH_PATCHSET1_CRMCONFIG XPATH_PATCHSET1_DIFF "//" XML_CIB_TAG_CRMCONFIG
#define XPATH_PATCHSET1_ALERTS XPATH_PATCHSET1_DIFF "//" XML_CIB_TAG_ALERTS
#define XPATH_PATCHSET1_EITHER \
XPATH_PATCHSET1_CRMCONFIG " | " XPATH_PATCHSET1_ALERTS
#define XPATH_CONFIG "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
#define XPATH_CRMCONFIG XPATH_CONFIG "/" XML_CIB_TAG_CRMCONFIG "/"
#define XPATH_ALERTS XPATH_CONFIG "/" XML_CIB_TAG_ALERTS
/*!
* \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
*/
bool
crm_patchset_contains_alert(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;
CRM_CHECK(msg != NULL, return FALSE);
crm_element_value_int(msg, F_CIB_RC, &rc);
if (rc < pcmk_ok) {
crm_trace("Ignore failed CIB update: %s (%d)", pcmk_strerror(rc), rc);
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 = __xml_first_child(patchset); change != NULL; change = __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 ... */
xmlNode *section = NULL;
const char *name = NULL;
if ((strcmp(xpath, XPATH_CONFIG) != 0) ||
((section = __xml_first_child(change)) == NULL) ||
((name = crm_element_name(section)) == NULL) ||
(strcmp(name, XML_CIB_TAG_ALERTS) != 0)) {
/* ... nor is it a newly added alerts section */
continue;
}
}
return TRUE;
}
} else {
crm_warn("Unknown patch format: %d", format);
}
return FALSE;
}
diff --git a/lib/lrmd/lrmd_alerts.c b/lib/lrmd/lrmd_alerts.c
index ae78c77c6c..7c674bb2d9 100644
--- a/lib/lrmd/lrmd_alerts.c
+++ b/lib/lrmd/lrmd_alerts.c
@@ -1,367 +1,378 @@
/*
* Copyright (c) 2015 David Vossel
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static lrmd_key_value_t *
alert_key2param(lrmd_key_value_t *head, enum crm_alert_keys_e name,
const char *value)
{
const char **key;
if (value == NULL) {
value = "";
}
for (key = crm_alert_keys[name]; *key; key++) {
crm_trace("Setting alert key %s = '%s'", *key, value);
head = lrmd_key_value_add(head, *key, value);
}
return head;
}
static lrmd_key_value_t *
alert_key2param_int(lrmd_key_value_t *head, enum crm_alert_keys_e name,
int value)
{
char *value_s = crm_itoa(value);
head = alert_key2param(head, name, value_s);
free(value_s);
return head;
}
static void
set_ev_kv(gpointer key, gpointer value, gpointer user_data)
{
lrmd_key_value_t **head = (lrmd_key_value_t **) user_data;
if (value) {
crm_trace("Setting environment variable %s='%s'",
(char*)key, (char*)value);
*head = lrmd_key_value_add(*head, key, value);
}
}
static lrmd_key_value_t *
alert_envvar2params(lrmd_key_value_t *head, crm_alert_entry_t *entry)
{
if (entry->envvars) {
g_hash_table_foreach(entry->envvars, set_ev_kv, &head);
}
return head;
}
/*
* We could use g_strv_contains() instead of this function,
* but that has only been available since glib 2.43.2.
*/
static gboolean
is_target_alert(char **list, const char *value)
{
int target_list_num = 0;
gboolean rc = FALSE;
CRM_CHECK(value != NULL, return FALSE);
if (list == NULL) {
return TRUE;
}
target_list_num = g_strv_length(list);
for (int cnt = 0; cnt < target_list_num; cnt++) {
if (strcmp(list[cnt], value) == 0) {
rc = TRUE;
break;
}
}
return rc;
}
/*!
* \internal
* \brief Execute alert agents for an event
*
* \param[in] lrmd LRMD connection to use
* \param[in] alert_list Alerts to execute
* \param[in] kind Type of event that is being alerted for
* \param[in] attr_name If crm_alert_attribute, the attribute name
* \param[in,out] params Environment variables to pass to agents
*
* \retval pcmk_ok on success
* \retval -1 if some alerts failed
* \retval -2 if all alerts failed
*/
static int
exec_alert_list(lrmd_t *lrmd, GList *alert_list, enum crm_alert_flags kind,
const char *attr_name, lrmd_key_value_t *params)
{
bool any_success = FALSE, any_failure = FALSE;
const char *kind_s = crm_alert_flag2text(kind);
crm_time_hr_t *now = NULL;
+ struct timeval tv_now;
+ char timestamp_epoch[20];
+ char timestamp_usec[7];
params = alert_key2param(params, CRM_alert_kind, kind_s);
params = alert_key2param(params, CRM_alert_version, VERSION);
for (GList *iter = g_list_first(alert_list); iter; iter = g_list_next(iter)) {
crm_alert_entry_t *entry = (crm_alert_entry_t *)(iter->data);
lrmd_key_value_t *copy_params = NULL;
lrmd_key_value_t *head = NULL;
int rc;
if (is_not_set(entry->flags, kind)) {
crm_trace("Filtering unwanted %s alert to %s via %s",
kind_s, entry->recipient, entry->id);
continue;
}
if ((kind == crm_alert_attribute)
&& !is_target_alert(entry->select_attribute_name, attr_name)) {
crm_trace("Filtering unwanted attribute '%s' alert to %s via %s",
attr_name, entry->recipient, entry->id);
continue;
}
if (now == NULL) {
now = crm_time_hr_new(NULL);
+ if (gettimeofday(&tv_now, NULL) == 0) {
+ now = crm_time_timeval_hr_convert(NULL, &tv_now);
+ }
}
crm_info("Sending %s alert via %s to %s",
kind_s, entry->id, entry->recipient);
/* Make a copy of the parameters, because each alert will be unique */
for (head = params; head != NULL; head = head->next) {
copy_params = lrmd_key_value_add(copy_params, head->key, head->value);
}
copy_params = alert_key2param(copy_params, CRM_alert_recipient,
entry->recipient);
if (now) {
char *timestamp = crm_time_format_hr(entry->tstamp_format, now);
if (timestamp) {
copy_params = alert_key2param(copy_params, CRM_alert_timestamp,
timestamp);
free(timestamp);
}
+
+ snprintf(timestamp_epoch, sizeof(timestamp_epoch), "%ld", tv_now.tv_sec);
+ copy_params = alert_key2param(copy_params, CRM_alert_timestamp_epoch, timestamp_epoch);
+ snprintf(timestamp_usec, sizeof(timestamp_usec), "%06d", now->useconds);
+ copy_params = alert_key2param(copy_params, CRM_alert_timestamp_usec, timestamp_usec);
}
copy_params = alert_envvar2params(copy_params, entry);
rc = lrmd->cmds->exec_alert(lrmd, entry->id, entry->path,
entry->timeout, copy_params);
if (rc < 0) {
crm_err("Could not execute alert %s: %s " CRM_XS " rc=%d",
entry->id, pcmk_strerror(rc), rc);
any_failure = TRUE;
} else {
any_success = TRUE;
}
}
if (now) {
free(now);
}
if (any_failure) {
return (any_success? -1 : -2);
}
return pcmk_ok;
}
/*!
* \internal
* \brief Send an alert for a node attribute change
*
* \param[in] lrmd LRMD connection to use
* \param[in] alert_list List of alert agents to execute
* \param[in] node Name of node with attribute change
* \param[in] nodeid Node ID of node with attribute change
* \param[in] attr_name Name of attribute that changed
* \param[in] attr_value New value of attribute that changed
*
* \retval pcmk_ok on success
* \retval -1 if some alert agents failed
* \retval -2 if all alert agents failed
*/
int
lrmd_send_attribute_alert(lrmd_t *lrmd, GList *alert_list,
const char *node, uint32_t nodeid,
const char *attr_name, const char *attr_value)
{
int rc = pcmk_ok;
lrmd_key_value_t *params = NULL;
if (lrmd == NULL) {
return -2;
}
params = alert_key2param(params, CRM_alert_node, node);
params = alert_key2param_int(params, CRM_alert_nodeid, nodeid);
params = alert_key2param(params, CRM_alert_attribute_name, attr_name);
params = alert_key2param(params, CRM_alert_attribute_value, attr_value);
rc = exec_alert_list(lrmd, alert_list, crm_alert_attribute, attr_name,
params);
lrmd_key_value_freeall(params);
return rc;
}
/*!
* \internal
* \brief Send an alert for a node membership event
*
* \param[in] lrmd LRMD connection to use
* \param[in] alert_list List of alert agents to execute
* \param[in] node Name of node with change
* \param[in] nodeid Node ID of node with change
* \param[in] state New state of node with change
*
* \retval pcmk_ok on success
* \retval -1 if some alert agents failed
* \retval -2 if all alert agents failed
*/
int
lrmd_send_node_alert(lrmd_t *lrmd, GList *alert_list,
const char *node, uint32_t nodeid, const char *state)
{
int rc = pcmk_ok;
lrmd_key_value_t *params = NULL;
if (lrmd == NULL) {
return -2;
}
params = alert_key2param(params, CRM_alert_node, node);
params = alert_key2param(params, CRM_alert_desc, state);
params = alert_key2param_int(params, CRM_alert_nodeid, nodeid);
rc = exec_alert_list(lrmd, alert_list, crm_alert_node, NULL, params);
lrmd_key_value_freeall(params);
return rc;
}
/*!
* \internal
* \brief Send an alert for a fencing event
*
* \param[in] lrmd LRMD connection to use
* \param[in] alert_list List of alert agents to execute
* \param[in] target Name of fence target node
* \param[in] task Type of fencing event that occurred
* \param[in] desc Readable description of event
* \param[in] op_rc Result of fence action
*
* \retval pcmk_ok on success
* \retval -1 if some alert agents failed
* \retval -2 if all alert agents failed
*/
int
lrmd_send_fencing_alert(lrmd_t *lrmd, GList *alert_list,
const char *target, const char *task, const char *desc,
int op_rc)
{
int rc = pcmk_ok;
lrmd_key_value_t *params = NULL;
if (lrmd == NULL) {
return -2;
}
params = alert_key2param(params, CRM_alert_node, target);
params = alert_key2param(params, CRM_alert_task, task);
params = alert_key2param(params, CRM_alert_desc, desc);
params = alert_key2param_int(params, CRM_alert_rc, op_rc);
rc = exec_alert_list(lrmd, alert_list, crm_alert_fencing, NULL, params);
lrmd_key_value_freeall(params);
return rc;
}
/*!
* \internal
* \brief Send an alert for a resource operation
*
* \param[in] lrmd LRMD connection to use
* \param[in] alert_list List of alert agents to execute
* \param[in] node Name of node that executed operation
* \param[in] op Resource operation
*
* \retval pcmk_ok on success
* \retval -1 if some alert agents failed
* \retval -2 if all alert agents failed
*/
int
lrmd_send_resource_alert(lrmd_t *lrmd, GList *alert_list,
const char *node, lrmd_event_data_t *op)
{
int rc = pcmk_ok;
int target_rc = pcmk_ok;
lrmd_key_value_t *params = NULL;
if (lrmd == NULL) {
return -2;
}
target_rc = rsc_op_expected_rc(op);
if ((op->interval == 0) && (target_rc == op->rc)
&& safe_str_eq(op->op_type, RSC_STATUS)) {
/* Don't send alerts for probes with the expected result. Leave it up to
* the agent whether to alert for 'failed' probes. (Even if we find a
* resource running, it was probably because someone did a clean-up of
* the status section.)
*/
return pcmk_ok;
}
params = alert_key2param(params, CRM_alert_node, node);
params = alert_key2param(params, CRM_alert_rsc, op->rsc_id);
params = alert_key2param(params, CRM_alert_task, op->op_type);
params = alert_key2param_int(params, CRM_alert_interval, op->interval);
params = alert_key2param_int(params, CRM_alert_target_rc, target_rc);
params = alert_key2param_int(params, CRM_alert_status, op->op_status);
params = alert_key2param_int(params, CRM_alert_rc, op->rc);
if (op->op_status == PCMK_LRM_OP_DONE) {
params = alert_key2param(params, CRM_alert_desc, services_ocf_exitcode_str(op->rc));
} else {
params = alert_key2param(params, CRM_alert_desc, services_lrm_status_str(op->op_status));
}
rc = exec_alert_list(lrmd, alert_list, crm_alert_resource, NULL, params);
lrmd_key_value_freeall(params);
return rc;
}