Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F2825234
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
34 KB
Referenced Files
None
Subscribers
None
View Options
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)
# ================================
# <configuration>
# <alerts>
# <alert id="snmp_alert" path="/path/to/alert_snmp.sh">
# <instance_attributes id="config_for_alert_snmp">
# <nvpair id="trap_node_states" name="trap_node_states" value="all"/>
# </instance_attributes>
# <meta_attributes id="config_for_timestamp">
# <nvpair id="ts_fmt" name="timestamp-format" value="%Y-%m-%d,%H:%M:%S.%01N"/>
# </meta_attributes>
# <recipient id="snmp_destination" value="192.168.1.2"/>
# </alert>
# </alerts>
# </configuration>
# ================================
#
# 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 <andrew@beekhof.net>
*
* 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 <glib.h>
#include <stdbool.h>
/* 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 <andrew@beekhof.net>
*
* 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 <crm_internal.h>
#include <crm/crm.h>
#include <crm/lrmd.h>
#include <crm/msg_xml.h>
#include <crm/common/alerts_internal.h>
#include <crm/cib/internal.h> /* 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 <davidvossel@gmail.com>
*
* 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 <crm_internal.h>
#include <glib.h>
#include <unistd.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/common/alerts_internal.h>
#include <crm/common/iso8601_internal.h>
#include <crm/lrmd_alerts_internal.h>
#include <crm/pengine/status.h>
#include <crm/cib.h>
#include <crm/lrmd.h>
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;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jan 25, 11:53 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1322449
Default Alt Text
(34 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment