pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
alerts.c
Go to the documentation of this file.
1/*
2 * Copyright 2015-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11#include <crm/crm.h>
12#include <crm/lrmd.h>
13#include <crm/common/xml.h>
17
19 [PCMK__alert_key_recipient] = "CRM_alert_recipient",
20 [PCMK__alert_key_node] = "CRM_alert_node",
21 [PCMK__alert_key_nodeid] = "CRM_alert_nodeid",
22 [PCMK__alert_key_rsc] = "CRM_alert_rsc",
23 [PCMK__alert_key_task] = "CRM_alert_task",
24 [PCMK__alert_key_interval] = "CRM_alert_interval",
25 [PCMK__alert_key_desc] = "CRM_alert_desc",
26 [PCMK__alert_key_status] = "CRM_alert_status",
27 [PCMK__alert_key_target_rc] = "CRM_alert_target_rc",
28 [PCMK__alert_key_rc] = "CRM_alert_rc",
29 [PCMK__alert_key_kind] = "CRM_alert_kind",
30 [PCMK__alert_key_version] = "CRM_alert_version",
32 [PCMK__alert_key_timestamp] = "CRM_alert_timestamp",
33 [PCMK__alert_key_attribute_name] = "CRM_alert_attribute_name",
34 [PCMK__alert_key_attribute_value] = "CRM_alert_attribute_value",
35 [PCMK__alert_key_timestamp_epoch] = "CRM_alert_timestamp_epoch",
36 [PCMK__alert_key_timestamp_usec] = "CRM_alert_timestamp_usec",
37 [PCMK__alert_key_exec_time] = "CRM_alert_exec_time",
38};
39
52pcmk__alert_new(const char *id, const char *path)
53{
55
56 pcmk__assert((id != NULL) && (path != NULL));
57 entry->id = pcmk__str_copy(id);
58 entry->path = pcmk__str_copy(path);
61 return entry;
62}
63
64void
66{
67 if (entry) {
68 free(entry->id);
69 free(entry->path);
70 free(entry->tstamp_format);
71 free(entry->recipient);
72
73 g_strfreev(entry->select_attribute_name);
74 if (entry->envvars) {
75 g_hash_table_destroy(entry->envvars);
76 }
77 free(entry);
78 }
79}
80
91{
92 pcmk__alert_t *new_entry = pcmk__alert_new(entry->id, entry->path);
93
94 new_entry->timeout = entry->timeout;
95 new_entry->flags = entry->flags;
96 new_entry->envvars = pcmk__str_table_dup(entry->envvars);
97 new_entry->tstamp_format = pcmk__str_copy(entry->tstamp_format);
98 new_entry->recipient = pcmk__str_copy(entry->recipient);
99 if (entry->select_attribute_name) {
100 new_entry->select_attribute_name = g_strdupv(entry->select_attribute_name);
101 }
102 return new_entry;
103}
104
105void
107 const char *value)
108{
109 pcmk__assert((table != NULL) && (name >= 0)
111 if (value == NULL) {
112 crm_trace("Removing alert key %s", pcmk__alert_keys[name]);
113 g_hash_table_remove(table, pcmk__alert_keys[name]);
114 } else {
115 crm_trace("Inserting alert key %s = '%s'",
116 pcmk__alert_keys[name], value);
117 pcmk__insert_dup(table, pcmk__alert_keys[name], value);
118 }
119}
120
121void
123 int value)
124{
125 pcmk__assert((table != NULL) && (name >= 0)
127 crm_trace("Inserting alert key %s = %d", pcmk__alert_keys[name], value);
128 g_hash_table_insert(table, pcmk__str_copy(pcmk__alert_keys[name]),
129 pcmk__itoa(value));
130}
131
132#define READABLE_DEFAULT pcmk__readable_interval(PCMK__ALERT_DEFAULT_TIMEOUT_MS)
133
145static int
146unpack_alert_options(xmlNode *xml, pcmk__alert_t *entry, guint *max_timeout)
147{
148 GHashTable *config_hash = pcmk__strkey_table(free, free);
149 crm_time_t *now = crm_time_new(NULL);
150 const char *value = NULL;
151 int rc = pcmk_rc_ok;
152
153 pcmk_rule_input_t rule_input = {
154 .now = now,
155 };
156
158 config_hash, NULL);
159 crm_time_free(now);
160
161 value = g_hash_table_lookup(config_hash, PCMK_META_ENABLED);
162 if ((value != NULL) && !crm_is_true(value)) {
163 // No need to continue unpacking
164 rc = pcmk_rc_disabled;
165 goto done;
166 }
167
168 value = g_hash_table_lookup(config_hash, PCMK_META_TIMEOUT);
169 if (value != NULL) {
170 long long timeout_ms = crm_get_msec(value);
171
172 entry->timeout = (int) QB_MIN(timeout_ms, INT_MAX);
173 if (entry->timeout <= 0) {
174 if (entry->timeout == 0) {
175 crm_trace("Alert %s uses default timeout (%s)",
176 entry->id, READABLE_DEFAULT);
177 } else {
178 pcmk__config_warn("Using default timeout (%s) for alert %s "
179 "because '%s' is not a valid timeout",
180 entry->id, value, READABLE_DEFAULT);
181 }
183 } else {
184 crm_trace("Alert %s uses timeout of %s",
185 entry->id, pcmk__readable_interval(entry->timeout));
186 }
187 if (entry->timeout > *max_timeout) {
188 *max_timeout = entry->timeout;
189 }
190 }
191 value = g_hash_table_lookup(config_hash, PCMK_META_TIMESTAMP_FORMAT);
192 if (value != NULL) {
193 /* hard to do any checks here as merely anything can
194 * can be a valid time-format-string
195 */
196 entry->tstamp_format = strdup(value);
197 crm_trace("Alert %s uses timestamp format '%s'",
198 entry->id, entry->tstamp_format);
199 }
200
201done:
202 g_hash_table_destroy(config_hash);
203 return rc;
204}
205
214static void
215unpack_alert_parameters(const xmlNode *xml, pcmk__alert_t *entry)
216{
217 xmlNode *child;
218
219 if ((xml == NULL) || (entry == NULL)) {
220 return;
221 }
222
224 NULL);
225 if (child == NULL) {
226 return;
227 }
228
229 if (entry->envvars == NULL) {
230 entry->envvars = pcmk__strkey_table(free, free);
231 }
232
233 for (child = pcmk__xe_first_child(child, PCMK_XE_NVPAIR, NULL, NULL);
234 child != NULL; child = pcmk__xe_next(child, PCMK_XE_NVPAIR)) {
235
236 const char *name = crm_element_value(child, PCMK_XA_NAME);
237 const char *value = crm_element_value(child, PCMK_XA_VALUE);
238
239 if (value == NULL) {
240 value = "";
241 }
242 pcmk__insert_dup(entry->envvars, name, value);
243 crm_trace("Alert %s: added environment variable %s='%s'",
244 entry->id, name, value);
245 }
246}
247
256static void
257unpack_alert_filter(xmlNode *xml, pcmk__alert_t *entry)
258{
259 xmlNode *select = pcmk__xe_first_child(xml, PCMK_XE_SELECT, NULL, NULL);
260 xmlNode *event_type = NULL;
261 uint32_t flags = pcmk__alert_none;
262
263 for (event_type = pcmk__xe_first_child(select, NULL, NULL, NULL);
264 event_type != NULL; event_type = pcmk__xe_next(event_type, NULL)) {
265
266 if (pcmk__xe_is(event_type, PCMK_XE_SELECT_FENCING)) {
268
269 } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_NODES)) {
271
272 } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_RESOURCES)) {
274
275 } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_ATTRIBUTES)) {
276 xmlNode *attr;
277 const char *attr_name;
278 int nattrs = 0;
279
281 for (attr = pcmk__xe_first_child(event_type, PCMK_XE_ATTRIBUTE,
282 NULL, NULL);
283 attr != NULL; attr = pcmk__xe_next(attr, PCMK_XE_ATTRIBUTE)) {
284
285 attr_name = crm_element_value(attr, PCMK_XA_NAME);
286 if (attr_name) {
287 if (nattrs == 0) {
288 g_strfreev(entry->select_attribute_name);
289 entry->select_attribute_name = NULL;
290 }
291 ++nattrs;
292 entry->select_attribute_name = pcmk__realloc(entry->select_attribute_name,
293 (nattrs + 1) * sizeof(char*));
294 entry->select_attribute_name[nattrs - 1] = strdup(attr_name);
295 entry->select_attribute_name[nattrs] = NULL;
296 }
297 }
298 }
299 }
300
301 if (flags != pcmk__alert_none) {
302 entry->flags = flags;
303 crm_debug("Alert %s receives events: attributes:%s%s%s%s",
304 entry->id,
306 (entry->select_attribute_name? "some" : "all") : "none"),
307 (pcmk_is_set(flags, pcmk__alert_fencing)? " fencing" : ""),
308 (pcmk_is_set(flags, pcmk__alert_node)? " nodes" : ""),
309 (pcmk_is_set(flags, pcmk__alert_resource)? " resources" : ""));
310 }
311}
312
323static int
324unpack_alert(xmlNode *alert, pcmk__alert_t *entry, guint *max_timeout)
325{
326 int rc = pcmk_rc_ok;
327
328 unpack_alert_parameters(alert, entry);
329 rc = unpack_alert_options(alert, entry, max_timeout);
330 if (rc == pcmk_rc_ok) {
331 unpack_alert_filter(alert, entry);
332 }
333 return rc;
334}
335
344GList *
345pcmk__unpack_alerts(const xmlNode *alerts)
346{
347 xmlNode *alert;
348 pcmk__alert_t *entry;
349 guint max_timeout = 0U;
350 GList *alert_list = NULL;
351
352 for (alert = pcmk__xe_first_child(alerts, PCMK_XE_ALERT, NULL, NULL);
353 alert != NULL; alert = pcmk__xe_next(alert, PCMK_XE_ALERT)) {
354
355 xmlNode *recipient = NULL;
356 int recipients = 0;
357 const char *alert_id = pcmk__xe_id(alert);
358 const char *alert_path = crm_element_value(alert, PCMK_XA_PATH);
359
360 // Not possible with schema validation enabled
361 if (alert_id == NULL) {
362 pcmk__config_err("Ignoring invalid alert without " PCMK_XA_ID);
363 continue;
364 }
365 if (alert_path == NULL) {
366 pcmk__config_err("Ignoring invalid alert %s without " PCMK_XA_PATH,
367 alert_id);
368 continue;
369 }
370
371 entry = pcmk__alert_new(alert_id, alert_path);
372
373 if (unpack_alert(alert, entry, &max_timeout) != pcmk_rc_ok) {
374 // Don't allow recipients to override if entire alert is disabled
375 crm_debug("Alert %s is disabled", entry->id);
376 pcmk__free_alert(entry);
377 continue;
378 }
379
380 if (entry->tstamp_format == NULL) {
381 entry->tstamp_format =
383 }
384
385 crm_debug("Alert %s: path=%s timeout=%s tstamp-format='%s'",
386 entry->id, entry->path,
388 entry->tstamp_format);
389
390 for (recipient = pcmk__xe_first_child(alert, PCMK_XE_RECIPIENT, NULL,
391 NULL);
392 recipient != NULL;
393 recipient = pcmk__xe_next(recipient, PCMK_XE_RECIPIENT)) {
394
395 pcmk__alert_t *recipient_entry = pcmk__dup_alert(entry);
396
397 recipients++;
398 recipient_entry->recipient = crm_element_value_copy(recipient,
400
401 if (unpack_alert(recipient, recipient_entry,
402 &max_timeout) != pcmk_rc_ok) {
403 crm_debug("Alert %s: recipient %s is disabled",
404 entry->id, recipient_entry->id);
405 pcmk__free_alert(recipient_entry);
406 continue;
407 }
408 alert_list = g_list_prepend(alert_list, recipient_entry);
409 crm_debug("Alert %s has recipient %s with value %s and %d envvars",
410 entry->id, pcmk__xe_id(recipient),
411 recipient_entry->recipient,
412 (recipient_entry->envvars?
413 g_hash_table_size(recipient_entry->envvars) : 0));
414 }
415
416 if (recipients == 0) {
417 alert_list = g_list_prepend(alert_list, entry);
418 } else { // Recipients were prepended individually above
419 pcmk__free_alert(entry);
420 }
421 }
422 return alert_list;
423}
424
431void
432pcmk__free_alerts(GList *alert_list)
433{
434 if (alert_list != NULL) {
435 g_list_free_full(alert_list, (GDestroyNotify) pcmk__free_alert);
436 }
437}
GList * pcmk__unpack_alerts(const xmlNode *alerts)
Definition alerts.c:345
void pcmk__free_alert(pcmk__alert_t *entry)
Definition alerts.c:65
pcmk__alert_t * pcmk__alert_new(const char *id, const char *path)
Create a new alert entry structure.
Definition alerts.c:52
pcmk__alert_t * pcmk__dup_alert(const pcmk__alert_t *entry)
Definition alerts.c:90
const char * pcmk__alert_keys[PCMK__ALERT_INTERNAL_KEY_MAX]
Definition alerts.c:18
void pcmk__free_alerts(GList *alert_list)
Definition alerts.c:432
void pcmk__add_alert_key_int(GHashTable *table, enum pcmk__alert_keys_e name, int value)
Definition alerts.c:122
#define READABLE_DEFAULT
Definition alerts.c:132
void pcmk__add_alert_key(GHashTable *table, enum pcmk__alert_keys_e name, const char *value)
Definition alerts.c:106
#define PCMK__ALERT_DEFAULT_TSTAMP_FORMAT
#define PCMK__ALERT_NODE_SEQUENCE
pcmk__alert_keys_e
@ PCMK__alert_key_node
@ PCMK__alert_key_task
@ PCMK__alert_key_timestamp_epoch
@ PCMK__alert_key_rc
@ PCMK__alert_key_status
@ PCMK__alert_key_nodeid
@ PCMK__alert_key_interval
@ PCMK__alert_key_timestamp
@ PCMK__alert_key_attribute_name
@ PCMK__alert_key_target_rc
@ PCMK__alert_key_timestamp_usec
@ PCMK__alert_key_kind
@ PCMK__alert_key_node_sequence
@ PCMK__alert_key_rsc
@ PCMK__alert_key_recipient
@ PCMK__alert_key_exec_time
@ PCMK__alert_key_version
@ PCMK__alert_key_desc
@ PCMK__alert_key_attribute_value
#define PCMK__ALERT_DEFAULT_TIMEOUT_MS
@ pcmk__alert_resource
@ pcmk__alert_default
@ pcmk__alert_none
@ pcmk__alert_attribute
@ pcmk__alert_fencing
@ pcmk__alert_node
#define PCMK__ALERT_INTERNAL_KEY_MAX
const char * path
Definition cib.c:28
const char * name
Definition cib.c:26
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
uint64_t flags
Definition remote.c:3
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
A dumping ground.
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:150
crm_time_t * crm_time_new(const char *string)
Definition iso8601.c:112
struct crm_time_s crm_time_t
Definition iso8601.h:32
const char * pcmk__readable_interval(guint interval_ms)
Definition iso8601.c:2210
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_trace(fmt, args...)
Definition logging.h:370
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
Resource agent executor.
void pcmk_unpack_nvpair_blocks(const xmlNode *xml, const char *element_name, const char *first_id, const pcmk_rule_input_t *rule_input, GHashTable *values, crm_time_t *next_change)
Unpack nvpair blocks contained by an XML element into a hash table, evaluated for any rules.
Definition nvpair.c:483
#define PCMK_META_TIMESTAMP_FORMAT
Definition options.h:116
#define PCMK_META_ENABLED
Definition options.h:88
#define PCMK_META_TIMEOUT
Definition options.h:115
@ pcmk_rc_disabled
Definition results.h:120
@ pcmk_rc_ok
Definition results.h:159
#define pcmk__assert(expr)
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition strings.c:351
gboolean crm_is_true(const char *s)
Definition strings.c:490
GHashTable * pcmk__str_table_dup(GHashTable *old_table)
Definition strings.c:768
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition strings.c:703
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:685
#define pcmk__str_copy(str)
char ** select_attribute_name
GHashTable * envvars
Data used to evaluate a rule (any NULL items are ignored)
Definition rules.h:57
Wrappers for and extensions to libxml2.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
#define PCMK_XE_SELECT_RESOURCES
Definition xml_names.h:197
#define PCMK_XE_SELECT_NODES
Definition xml_names.h:196
#define PCMK_XE_RECIPIENT
Definition xml_names.h:170
#define PCMK_XE_SELECT
Definition xml_names.h:193
#define PCMK_XA_PATH
Definition xml_names.h:355
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XE_ATTRIBUTE
Definition xml_names.h:69
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition xml_names.h:122
#define PCMK_XE_META_ATTRIBUTES
Definition xml_names.h:130
#define PCMK_XA_VALUE
Definition xml_names.h:442
#define PCMK_XE_SELECT_FENCING
Definition xml_names.h:195
#define PCMK_XE_ALERT
Definition xml_names.h:65
#define PCMK_XE_NVPAIR
Definition xml_names.h:144
#define PCMK_XA_NAME
Definition xml_names.h:330
#define PCMK_XE_SELECT_ATTRIBUTES
Definition xml_names.h:194