pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
rules_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/common/xml.h>
13 #include <crm/pengine/rules.h>
17 
28 static int
29 get_meta_attrs_from_cib(xmlNode *basenode, pcmk__alert_t *entry,
30  guint *max_timeout)
31 {
32  GHashTable *config_hash = pcmk__strkey_table(free, free);
33  crm_time_t *now = crm_time_new(NULL);
34  const char *value = NULL;
35  int rc = pcmk_rc_ok;
36 
37  pe_unpack_nvpairs(basenode, basenode, PCMK_XE_META_ATTRIBUTES, NULL,
38  config_hash, NULL, FALSE, now, NULL);
39  crm_time_free(now);
40 
41  value = g_hash_table_lookup(config_hash, PCMK_META_ENABLED);
42  if ((value != NULL) && !crm_is_true(value)) {
43  // No need to continue unpacking
44  rc = pcmk_rc_disabled;
45  goto done;
46  }
47 
48  value = g_hash_table_lookup(config_hash, PCMK_META_TIMEOUT);
49  if (value) {
50  long long timeout_ms = crm_get_msec(value);
51 
52  entry->timeout = (int) QB_MIN(timeout_ms, INT_MAX);
53  if (entry->timeout <= 0) {
54  if (entry->timeout == 0) {
55  crm_trace("Alert %s uses default timeout of %dmsec",
57  } else {
58  pcmk__config_warn("Alert %s has invalid timeout value '%s', "
59  "using default (%d ms)",
60  entry->id, value,
62  }
64  } else {
65  crm_trace("Alert %s uses timeout of %dmsec",
66  entry->id, entry->timeout);
67  }
68  if (entry->timeout > *max_timeout) {
69  *max_timeout = entry->timeout;
70  }
71  }
72  value = g_hash_table_lookup(config_hash, PCMK_META_TIMESTAMP_FORMAT);
73  if (value) {
74  /* hard to do any checks here as merely anything can
75  * can be a valid time-format-string
76  */
77  entry->tstamp_format = strdup(value);
78  crm_trace("Alert %s uses timestamp format '%s'",
79  entry->id, entry->tstamp_format);
80  }
81 
82 done:
83  g_hash_table_destroy(config_hash);
84  return rc;
85 }
86 
87 static void
88 get_envvars_from_cib(xmlNode *basenode, pcmk__alert_t *entry)
89 {
90  xmlNode *child;
91 
92  if ((basenode == NULL) || (entry == NULL)) {
93  return;
94  }
95 
96  child = pcmk__xe_first_child(basenode, PCMK_XE_INSTANCE_ATTRIBUTES, NULL,
97  NULL);
98  if (child == NULL) {
99  return;
100  }
101 
102  if (entry->envvars == NULL) {
103  entry->envvars = pcmk__strkey_table(free, free);
104  }
105 
106  for (child = pcmk__xe_first_child(child, PCMK_XE_NVPAIR, NULL, NULL);
107  child != NULL; child = pcmk__xe_next_same(child)) {
108 
109  const char *name = crm_element_value(child, PCMK_XA_NAME);
110  const char *value = crm_element_value(child, PCMK_XA_VALUE);
111 
112  if (value == NULL) {
113  value = "";
114  }
115  pcmk__insert_dup(entry->envvars, name, value);
116  crm_trace("Alert %s: added environment variable %s='%s'",
117  entry->id, name, value);
118  }
119 }
120 
121 static void
122 unpack_alert_filter(xmlNode *basenode, pcmk__alert_t *entry)
123 {
124  xmlNode *select = pcmk__xe_first_child(basenode, PCMK_XE_SELECT, NULL,
125  NULL);
126  xmlNode *event_type = NULL;
127  uint32_t flags = pcmk__alert_none;
128 
129  for (event_type = pcmk__xe_first_child(select, NULL, NULL, NULL);
130  event_type != NULL; event_type = pcmk__xe_next(event_type)) {
131 
132  if (pcmk__xe_is(event_type, PCMK_XE_SELECT_FENCING)) {
134 
135  } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_NODES)) {
137 
138  } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_RESOURCES)) {
140 
141  } else if (pcmk__xe_is(event_type, PCMK_XE_SELECT_ATTRIBUTES)) {
142  xmlNode *attr;
143  const char *attr_name;
144  int nattrs = 0;
145 
147  for (attr = pcmk__xe_first_child(event_type, PCMK_XE_ATTRIBUTE,
148  NULL, NULL);
149  attr != NULL; attr = pcmk__xe_next_same(attr)) {
150 
151  attr_name = crm_element_value(attr, PCMK_XA_NAME);
152  if (attr_name) {
153  if (nattrs == 0) {
154  g_strfreev(entry->select_attribute_name);
155  entry->select_attribute_name = NULL;
156  }
157  ++nattrs;
158  entry->select_attribute_name = pcmk__realloc(entry->select_attribute_name,
159  (nattrs + 1) * sizeof(char*));
160  entry->select_attribute_name[nattrs - 1] = strdup(attr_name);
161  entry->select_attribute_name[nattrs] = NULL;
162  }
163  }
164  }
165  }
166 
167  if (flags != pcmk__alert_none) {
168  entry->flags = flags;
169  crm_debug("Alert %s receives events: attributes:%s%s%s%s",
170  entry->id,
172  (entry->select_attribute_name? "some" : "all") : "none"),
173  (pcmk_is_set(flags, pcmk__alert_fencing)? " fencing" : ""),
174  (pcmk_is_set(flags, pcmk__alert_node)? " nodes" : ""),
175  (pcmk_is_set(flags, pcmk__alert_resource)? " resources" : ""));
176  }
177 }
178 
189 static int
190 unpack_alert(xmlNode *alert, pcmk__alert_t *entry, guint *max_timeout)
191 {
192  int rc = pcmk_rc_ok;
193 
194  get_envvars_from_cib(alert, entry);
195  rc = get_meta_attrs_from_cib(alert, entry, max_timeout);
196  if (rc == pcmk_rc_ok) {
197  unpack_alert_filter(alert, entry);
198  }
199  return rc;
200 }
201 
213 GList *
214 pe_unpack_alerts(const xmlNode *alerts)
215 {
216  xmlNode *alert;
217  pcmk__alert_t *entry;
218  guint max_timeout = 0;
219  GList *alert_list = NULL;
220 
221  if (alerts == NULL) {
222  return alert_list;
223  }
224 
225  for (alert = pcmk__xe_first_child(alerts, PCMK_XE_ALERT, NULL, NULL);
226  alert != NULL; alert = pcmk__xe_next_same(alert)) {
227 
228  xmlNode *recipient;
229  int recipients = 0;
230  const char *alert_id = pcmk__xe_id(alert);
231  const char *alert_path = crm_element_value(alert, PCMK_XA_PATH);
232 
233  /* The schema should enforce this, but to be safe ... */
234  if (alert_id == NULL) {
235  pcmk__config_warn("Ignoring invalid alert without " PCMK_XA_ID);
236  crm_log_xml_info(alert, "missing-id");
237  continue;
238  }
239  if (alert_path == NULL) {
240  pcmk__config_warn("Ignoring alert %s: No " PCMK_XA_PATH, alert_id);
241  continue;
242  }
243 
244  entry = pcmk__alert_new(alert_id, alert_path);
245 
246  if (unpack_alert(alert, entry, &max_timeout) != pcmk_rc_ok) {
247  // Don't allow recipients to override if entire alert is disabled
248  crm_debug("Alert %s is disabled", entry->id);
249  pcmk__free_alert(entry);
250  continue;
251  }
252 
253  if (entry->tstamp_format == NULL) {
255  }
256 
257  crm_debug("Alert %s: path=%s timeout=%dms tstamp-format='%s' %u vars",
258  entry->id, entry->path, entry->timeout, entry->tstamp_format,
259  (entry->envvars? g_hash_table_size(entry->envvars) : 0));
260 
261  for (recipient = pcmk__xe_first_child(alert, PCMK_XE_RECIPIENT, NULL,
262  NULL);
263  recipient != NULL; recipient = pcmk__xe_next_same(recipient)) {
264 
265  pcmk__alert_t *recipient_entry = pcmk__dup_alert(entry);
266 
267  recipients++;
268  recipient_entry->recipient = crm_element_value_copy(recipient,
269  PCMK_XA_VALUE);
270 
271  if (unpack_alert(recipient, recipient_entry,
272  &max_timeout) != pcmk_rc_ok) {
273  crm_debug("Alert %s: recipient %s is disabled",
274  entry->id, recipient_entry->id);
275  pcmk__free_alert(recipient_entry);
276  continue;
277  }
278  alert_list = g_list_prepend(alert_list, recipient_entry);
279  crm_debug("Alert %s has recipient %s with value %s and %d envvars",
280  entry->id, pcmk__xe_id(recipient),
281  recipient_entry->recipient,
282  (recipient_entry->envvars?
283  g_hash_table_size(recipient_entry->envvars) : 0));
284  }
285 
286  if (recipients == 0) {
287  alert_list = g_list_prepend(alert_list, entry);
288  } else {
289  pcmk__free_alert(entry);
290  }
291  }
292  return alert_list;
293 }
294 
301 void
302 pe_free_alert_list(GList *alert_list)
303 {
304  if (alert_list) {
305  g_list_free_full(alert_list, (GDestroyNotify) pcmk__free_alert);
306  }
307 }
A dumping ground.
#define PCMK_XE_NVPAIR
Definition: xml_names.h:141
#define PCMK_XA_NAME
Definition: xml_names.h:325
#define PCMK_XA_PATH
Definition: xml_names.h:350
const char * name
Definition: cib.c:26
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define PCMK_XE_ALERT
Definition: xml_names.h:65
#define pcmk__config_warn(fmt...)
void pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition: rules.c:270
#define PCMK_XE_RECIPIENT
Definition: xml_names.h:166
char ** select_attribute_name
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:356
char * tstamp_format
#define PCMK_XE_SELECT_FENCING
Definition: xml_names.h:191
#define PCMK_XE_ATTRIBUTE
Definition: xml_names.h:69
void pcmk__free_alert(pcmk__alert_t *entry)
Definition: alerts.c:108
#define PCMK_META_TIMESTAMP_FORMAT
Definition: options.h:115
#define crm_debug(fmt, args...)
Definition: logging.h:402
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:674
#define PCMK_XE_SELECT
Definition: xml_names.h:189
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:446
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:440
pcmk__alert_t * pcmk__dup_alert(const pcmk__alert_t *entry)
Definition: alerts.c:133
#define crm_trace(fmt, args...)
Definition: logging.h:404
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:98
Wrappers for and extensions to libxml2.
#define PCMK_META_ENABLED
Definition: options.h:87
#define PCMK_XA_ID
Definition: xml_names.h:296
#define PCMK_XA_VALUE
Definition: xml_names.h:437
GHashTable * envvars
#define PCMK_META_TIMEOUT
Definition: options.h:114
#define PCMK_XE_SELECT_NODES
Definition: xml_names.h:192
#define PCMK_XE_META_ATTRIBUTES
Definition: xml_names.h:127
GList * pe_unpack_alerts(const xmlNode *alerts)
Definition: rules_alerts.c:214
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:683
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:112
#define PCMK_XE_SELECT_ATTRIBUTES
Definition: xml_names.h:190
#define crm_log_xml_info(xml, text)
Definition: logging.h:410
void pe_free_alert_list(GList *alert_list)
Definition: rules_alerts.c:302
#define PCMK_XE_SELECT_RESOURCES
Definition: xml_names.h:193
pcmk__alert_t * pcmk__alert_new(const char *id, const char *path)
Create a new alert entry structure.
Definition: alerts.c:95
gboolean crm_is_true(const char *s)
Definition: strings.c:488
#define PCMK__ALERT_DEFAULT_TSTAMP_FORMAT
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition: xml_names.h:119
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2108
#define PCMK__ALERT_DEFAULT_TIMEOUT_MS
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition: strings.c:701
uint64_t flags
Definition: remote.c:215
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150