diff --git a/doc/sphinx/Pacemaker_Administration/alerts.rst b/doc/sphinx/Pacemaker_Administration/alerts.rst
index 42efc8d266..ea2b0f9ed2 100644
--- a/doc/sphinx/Pacemaker_Administration/alerts.rst
+++ b/doc/sphinx/Pacemaker_Administration/alerts.rst
@@ -1,311 +1,301 @@
.. index::
single: alert; agents
Alert Agents
------------
.. index::
single: alert; sample agents
Using the Sample Alert Agents
#############################
Pacemaker provides several sample alert agents, installed in
``/usr/share/pacemaker/alerts`` by default.
While these sample scripts may be copied and used as-is, they are provided
mainly as templates to be edited to suit your purposes. See their source code
for the full set of instance attributes they support.
.. topic:: Sending cluster events as SNMP v2c traps
.. code-block:: xml
.. note:: **SNMP alert agent attributes**
The ``timestamp-format`` meta-attribute should always be set to
``%Y-%m-%d,%H:%M:%S.%01N`` when using the SNMP agent, to match the SNMP
standard.
The SNMP agent provides a number of instance attributes in addition to the
one used in the example above. The most useful are ``trap_version``, which
defaults to ``2c``, and ``trap_community``, which defaults to ``public``.
See the source code for more details.
.. topic:: Sending cluster events as SNMP v3 traps
.. code-block:: xml
.. note:: **SNMP v3 trap configuration**
To use SNMP v3, ``trap_version`` must be set to ``3``. ``trap_community``
will be ignored.
The example above uses the ``trap_options`` instance attribute to override
the security level, authentication protocol, authentication user, and
authentication password from snmp.conf. These will be passed to the snmptrap
command. Passing the password on the command line is considered insecure;
specify authentication and privacy options suitable for your environment.
.. topic:: Sending cluster events as e-mails
.. code-block:: xml
.. index::
single: alert; agent development
Writing an Alert Agent
######################
.. index::
single: alert; environment variables
single: environment variable; alert agents
.. table:: **Environment variables passed to alert agents**
:class: longtable
:widths: 1 3
+---------------------------+----------------------------------------------------------------+
| Environment Variable | Description |
+===========================+================================================================+
| CRM_alert_kind | .. index:: |
| | single:environment variable; CRM_alert_kind |
| | single:CRM_alert_kind |
| | |
| | The type of alert (``node``, ``fencing``, ``resource``, or |
| | ``attribute``) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_node | .. index:: |
| | single:environment variable; CRM_alert_node |
| | single:CRM_alert_node |
| | |
| | Name of affected node |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_node_sequence | .. index:: |
| | single:environment variable; CRM_alert_sequence |
| | single:CRM_alert_sequence |
| | |
| | A sequence number increased whenever an alert is being issued |
| | on the local node, which can be used to reference the order in |
| | which alerts have been issued by Pacemaker. An alert for an |
| | event that happened later in time reliably has a higher |
| | sequence number than alerts for earlier events. |
| | |
| | Be aware that this number has no cluster-wide meaning. |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_recipient | .. index:: |
| | single:environment variable; CRM_alert_recipient |
| | single:CRM_alert_recipient |
| | |
| | The configured recipient |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_timestamp | .. index:: |
| | single:environment variable; CRM_alert_timestamp |
| | single:CRM_alert_timestamp |
| | |
| | A timestamp created prior to executing the agent, in the |
| | format specified by the ``timestamp-format`` meta-attribute. |
| | This allows the agent to have a reliable, high-precision time |
| | of when the event occurred, regardless of when the agent |
| | itself was invoked (which could potentially be delayed due to |
| | system load, etc.). |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_timestamp_epoch | .. index:: |
| | single:environment variable; CRM_alert_timestamp_epoch |
| | single:CRM_alert_timestamp_epoch |
| | |
| | The same time as ``CRM_alert_timestamp``, expressed as the |
| | integer number of seconds since January 1, 1970. This (along |
| | with ``CRM_alert_timestamp_usec``) can be useful for alert |
| | agents that need to format time in a specific way rather than |
| | let the user configure it. |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_timestamp_usec | .. index:: |
| | single:environment variable; CRM_alert_timestamp_usec |
| | single:CRM_alert_timestamp_usec |
| | |
| | The same time as ``CRM_alert_timestamp``, expressed as the |
| | integer number of microseconds since |
| | ``CRM_alert_timestamp_epoch``. |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_version | .. index:: |
| | single:environment variable; CRM_alert_version |
| | single:CRM_alert_version |
| | |
| | The version of Pacemaker sending the alert |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_desc | .. index:: |
| | single:environment variable; CRM_alert_desc |
| | single:CRM_alert_desc |
| | |
| | Detail about event. For ``node`` alerts, this is the node's |
| | current state (``member`` or ``lost``). For ``fencing`` |
| | alerts, this is a summary of the requested fencing operation, |
| | including origin, target, and fencing operation error code, if |
| | any. For ``resource`` alerts, this is a readable string |
| | equivalent of ``CRM_alert_status``. |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_nodeid | .. index:: |
| | single:environment variable; CRM_alert_nodeid |
| | single:CRM_alert_nodeid |
| | |
| | ID of node whose status changed (provided with ``node`` alerts |
| | only) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_rc | .. index:: |
| | single:environment variable; CRM_alert_rc |
| | single:CRM_alert_rc |
| | |
| | The numerical return code of the fencing or resource operation |
| | (provided with ``fencing`` and ``resource`` alerts only) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_task | .. index:: |
| | single:environment variable; CRM_alert_task |
| | single:CRM_alert_task |
| | |
| | The requested fencing or resource operation (provided with |
| | ``fencing`` and ``resource`` alerts only) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_exec_time | .. index:: |
| | single:environment variable; CRM_alert_exec_time |
| | single:CRM_alert_exec_time |
| | |
| | The (wall-clock) time, in milliseconds, that it took to |
| | execute the action. If the action timed out, |
| | ``CRM_alert_status`` will be 2, ``CRM_alert_desc`` will be |
| | "Timed Out", and this value will be the action timeout. May |
| | not be supported on all platforms. (``resource`` alerts only) |
| | *(since 2.0.1)* |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_interval | .. index:: |
| | single:environment variable; CRM_alert_interval |
| | single:CRM_alert_interval |
| | |
| | The interval of the resource operation (``resource`` alerts |
| | only) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_rsc | .. index:: |
| | single:environment variable; CRM_alert_rsc |
| | single:CRM_alert_rsc |
| | |
| | The name of the affected resource (``resource`` alerts only) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_status | .. index:: |
| | single:environment variable; CRM_alert_status |
| | single:CRM_alert_status |
| | |
| | A numerical code used by Pacemaker to represent the operation |
| | result (``resource`` alerts only) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_target_rc | .. index:: |
| | single:environment variable; CRM_alert_target_rc |
| | single:CRM_alert_target_rc |
| | |
| | The expected numerical return code of the operation |
| | (``resource`` alerts only) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_attribute_name | .. index:: |
| | single:environment variable; CRM_alert_attribute_name |
| | single:CRM_alert_attribute_name |
| | |
| | The name of the node attribute that changed (``attribute`` |
| | alerts only) |
+---------------------------+----------------------------------------------------------------+
| CRM_alert_attribute_value | .. index:: |
| | single:environment variable; CRM_alert_attribute_value |
| | single:CRM_alert_attribute_value |
| | |
| | The new value of the node attribute that changed |
| | (``attribute`` alerts only) |
+---------------------------+----------------------------------------------------------------+
Special concerns when writing alert agents:
* Alert agents may be called with no recipient (if none is configured),
so the agent must be able to handle this situation, even if it
only exits in that case. (Users may modify the configuration in
stages, and add a recipient later.)
* If more than one recipient is configured for an alert, the alert agent will
be called once per recipient. If an agent is not able to run concurrently, it
should be configured with only a single recipient. The agent is free,
however, to interpret the recipient as a list.
* When a cluster event occurs, all alerts are fired off at the same time as
separate processes. Depending on how many alerts and recipients are
configured, and on what is done within the alert agents,
a significant load burst may occur. The agent could be written to take
this into consideration, for example by queueing resource-intensive actions
into some other instance, instead of directly executing them.
* Alert agents are run as the |CRM_DAEMON_USER| user, which has a minimal set
of permissions. If an agent requires additional privileges, it is
recommended to configure ``sudo`` to allow the agent to run the necessary
commands as another user with the appropriate privileges.
* As always, take care to validate and sanitize user-configured parameters,
such as ``CRM_alert_timestamp`` (whose content is specified by the
user-configured ``timestamp-format``), ``CRM_alert_recipient,`` and all
instance attributes. Mostly this is needed simply to protect against
configuration errors, but if some user can modify the CIB without having
|CRM_DAEMON_USER| access to the cluster nodes, it is a potential security
concern as well, to avoid the possibility of code injection.
-
-.. note:: **ocf:pacemaker:ClusterMon compatibility**
-
- The alerts interface is designed to be backward compatible with the external
- scripts interface used by the ``ocf:pacemaker:ClusterMon`` resource, which
- is now deprecated. To preserve this compatibility, the environment variables
- passed to alert agents are available prepended with ``CRM_notify_``
- as well as ``CRM_alert_``. One break in compatibility is that ``ClusterMon``
- ran external scripts as the ``root`` user, while alert agents are run as the
- |CRM_DAEMON_USER| user.
diff --git a/include/crm/common/alerts_internal.h b/include/crm/common/alerts_internal.h
index 783bc883d0..e10af1ae1d 100644
--- a/include/crm/common/alerts_internal.h
+++ b/include/crm/common/alerts_internal.h
@@ -1,101 +1,101 @@
/*
* Copyright 2015-2024 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__CRM_COMMON_ALERTS_INTERNAL__H
#define PCMK__CRM_COMMON_ALERTS_INTERNAL__H
#include
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
/* 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];
+extern const char *pcmk__alert_keys[PCMK__ALERT_INTERNAL_KEY_MAX];
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);
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";
}
}
#ifdef __cplusplus
}
#endif
#endif // PCMK__CRM_COMMON_ALERTS_INTERNAL__H
diff --git a/lib/common/alerts.c b/lib/common/alerts.c
index eac3e2e953..0c685f0a9f 100644
--- a/lib/common/alerts.c
+++ b/lib/common/alerts.c
@@ -1,170 +1,130 @@
/*
* Copyright 2015-2024 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
-/*
- * 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
- }
+const char *pcmk__alert_keys[PCMK__ALERT_INTERNAL_KEY_MAX] = {
+ [PCMK__alert_key_recipient] = "CRM_alert_recipient",
+ [PCMK__alert_key_node] = "CRM_alert_node",
+ [PCMK__alert_key_nodeid] = "CRM_alert_nodeid",
+ [PCMK__alert_key_rsc] = "CRM_alert_rsc",
+ [PCMK__alert_key_task] = "CRM_alert_task",
+ [PCMK__alert_key_interval] = "CRM_alert_interval",
+ [PCMK__alert_key_desc] = "CRM_alert_desc",
+ [PCMK__alert_key_status] = "CRM_alert_status",
+ [PCMK__alert_key_target_rc] = "CRM_alert_target_rc",
+ [PCMK__alert_key_rc] = "CRM_alert_rc",
+ [PCMK__alert_key_kind] = "CRM_alert_kind",
+ [PCMK__alert_key_version] = "CRM_alert_version",
+ [PCMK__alert_key_node_sequence] = PCMK__ALERT_NODE_SEQUENCE,
+ [PCMK__alert_key_timestamp] = "CRM_alert_timestamp",
+ [PCMK__alert_key_attribute_name] = "CRM_alert_attribute_name",
+ [PCMK__alert_key_attribute_value] = "CRM_alert_attribute_value",
+ [PCMK__alert_key_timestamp_epoch] = "CRM_alert_timestamp_epoch",
+ [PCMK__alert_key_timestamp_usec] = "CRM_alert_timestamp_usec",
+ [PCMK__alert_key_exec_time] = "CRM_alert_exec_time",
};
/*!
* \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 = pcmk__assert_alloc(1, sizeof(pcmk__alert_t));
CRM_ASSERT((id != NULL) && (path != NULL));
entry->id = pcmk__str_copy(id);
entry->path = pcmk__str_copy(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);
new_entry->tstamp_format = pcmk__str_copy(entry->tstamp_format);
new_entry->recipient = pcmk__str_copy(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) {
- pcmk__insert_dup(table, *key, value);
- } else {
- g_hash_table_remove(table, *key);
- }
+ CRM_ASSERT((table != NULL) && (name >= 0)
+ && (name < PCMK__ALERT_INTERNAL_KEY_MAX));
+ if (value == NULL) {
+ crm_trace("Removing alert key %s", pcmk__alert_keys[name]);
+ g_hash_table_remove(table, pcmk__alert_keys[name]);
+ } else {
+ crm_trace("Inserting alert key %s = '%s'",
+ pcmk__alert_keys[name], value);
+ pcmk__insert_dup(table, pcmk__alert_keys[name], value);
}
}
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, pcmk__str_copy(*key), pcmk__itoa(value));
- }
+ CRM_ASSERT((table != NULL) && (name >= 0)
+ && (name < PCMK__ALERT_INTERNAL_KEY_MAX));
+ crm_trace("Inserting alert key %s = %d", pcmk__alert_keys[name], value);
+ g_hash_table_insert(table, pcmk__str_copy(pcmk__alert_keys[name]),
+ pcmk__itoa(value));
}
diff --git a/lib/lrmd/lrmd_alerts.c b/lib/lrmd/lrmd_alerts.c
index b73ba90f0f..f04fc958c0 100644
--- a/lib/lrmd/lrmd_alerts.c
+++ b/lib/lrmd/lrmd_alerts.c
@@ -1,399 +1,394 @@
/*
* Copyright 2015-2024 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
#include
#include
#include
#include
static lrmd_key_value_t *
alert_key2param(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
const char *value)
{
- const char **key;
-
if (value == NULL) {
value = "";
}
- for (key = pcmk__alert_keys[name]; *key; key++) {
- crm_trace("Setting alert key %s = '%s'", *key, value);
- head = lrmd_key_value_add(head, *key, value);
- }
- return head;
+ crm_trace("Setting alert key %s = '%s'", pcmk__alert_keys[name], value);
+ return lrmd_key_value_add(head, pcmk__alert_keys[name], value);
}
static lrmd_key_value_t *
alert_key2param_int(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
int value)
{
char *value_s = pcmk__itoa(value);
head = alert_key2param(head, name, value_s);
free(value_s);
return head;
}
static lrmd_key_value_t *
alert_key2param_ms(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
guint value)
{
char *value_s = crm_strdup_printf("%u", 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, const pcmk__alert_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,out] lrmd Executor 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 pcmk__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, const GList *alert_list,
enum pcmk__alert_flags kind, const char *attr_name,
lrmd_key_value_t *params)
{
bool any_success = FALSE, any_failure = FALSE;
const char *kind_s = pcmk__alert_flag2text(kind);
pcmk__time_hr_t *now = NULL;
char timestamp_epoch[20];
char timestamp_usec[7];
time_t epoch = 0;
params = alert_key2param(params, PCMK__alert_key_kind, kind_s);
params = alert_key2param(params, PCMK__alert_key_version,
PACEMAKER_VERSION);
for (const GList *iter = alert_list;
iter != NULL; iter = g_list_next(iter)) {
const pcmk__alert_t *entry = (pcmk__alert_t *) (iter->data);
lrmd_key_value_t *copy_params = NULL;
lrmd_key_value_t *head = NULL;
int rc;
if (!pcmk_is_set(entry->flags, kind)) {
crm_trace("Filtering unwanted %s alert to %s via %s",
kind_s, entry->recipient, entry->id);
continue;
}
if ((kind == pcmk__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 = pcmk__time_hr_now(&epoch);
}
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, PCMK__alert_key_recipient,
entry->recipient);
if (now) {
char *timestamp = pcmk__time_format_hr(entry->tstamp_format, now);
if (timestamp) {
copy_params = alert_key2param(copy_params,
PCMK__alert_key_timestamp,
timestamp);
free(timestamp);
}
snprintf(timestamp_epoch, sizeof(timestamp_epoch), "%lld",
(long long) epoch);
copy_params = alert_key2param(copy_params,
PCMK__alert_key_timestamp_epoch,
timestamp_epoch);
snprintf(timestamp_usec, sizeof(timestamp_usec), "%06d", now->useconds);
copy_params = alert_key2param(copy_params,
PCMK__alert_key_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 " QB_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,out] lrmd Executor 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, const 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, PCMK__alert_key_node, node);
params = alert_key2param_int(params, PCMK__alert_key_nodeid, nodeid);
params = alert_key2param(params, PCMK__alert_key_attribute_name, attr_name);
params = alert_key2param(params, PCMK__alert_key_attribute_value,
attr_value);
rc = exec_alert_list(lrmd, alert_list, pcmk__alert_attribute, attr_name,
params);
lrmd_key_value_freeall(params);
return rc;
}
/*!
* \internal
* \brief Send an alert for a node membership event
*
* \param[in,out] lrmd Executor 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, const 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, PCMK__alert_key_node, node);
params = alert_key2param(params, PCMK__alert_key_desc, state);
params = alert_key2param_int(params, PCMK__alert_key_nodeid, nodeid);
rc = exec_alert_list(lrmd, alert_list, pcmk__alert_node, NULL, params);
lrmd_key_value_freeall(params);
return rc;
}
/*!
* \internal
* \brief Send an alert for a fencing event
*
* \param[in,out] lrmd Executor 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, const 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, PCMK__alert_key_node, target);
params = alert_key2param(params, PCMK__alert_key_task, task);
params = alert_key2param(params, PCMK__alert_key_desc, desc);
params = alert_key2param_int(params, PCMK__alert_key_rc, op_rc);
rc = exec_alert_list(lrmd, alert_list, pcmk__alert_fencing, NULL, params);
lrmd_key_value_freeall(params);
return rc;
}
/*!
* \internal
* \brief Send an alert for a resource operation
*
* \param[in,out] lrmd Executor 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, const GList *alert_list,
const char *node, const 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_ms == 0) && (target_rc == op->rc)
&& pcmk__str_eq(op->op_type, PCMK_ACTION_MONITOR, pcmk__str_casei)) {
/* 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, PCMK__alert_key_node, node);
params = alert_key2param(params, PCMK__alert_key_rsc, op->rsc_id);
params = alert_key2param(params, PCMK__alert_key_task, op->op_type);
params = alert_key2param_ms(params, PCMK__alert_key_interval,
op->interval_ms);
params = alert_key2param_int(params, PCMK__alert_key_target_rc, target_rc);
params = alert_key2param_int(params, PCMK__alert_key_status, op->op_status);
params = alert_key2param_int(params, PCMK__alert_key_rc, op->rc);
/* Reoccurring operations do not set exec_time, so on timeout, set it
* to the operation timeout since that's closer to the actual value.
*/
if ((op->op_status == PCMK_EXEC_TIMEOUT) && (op->exec_time == 0)) {
params = alert_key2param_int(params, PCMK__alert_key_exec_time,
op->timeout);
} else {
params = alert_key2param_int(params, PCMK__alert_key_exec_time,
op->exec_time);
}
if (op->op_status == PCMK_EXEC_DONE) {
params = alert_key2param(params, PCMK__alert_key_desc,
services_ocf_exitcode_str(op->rc));
} else {
params = alert_key2param(params, PCMK__alert_key_desc,
pcmk_exec_status_str(op->op_status));
}
rc = exec_alert_list(lrmd, alert_list, pcmk__alert_resource, NULL, params);
lrmd_key_value_freeall(params);
return rc;
}