Line data Source code
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 :
12 : #include <glib.h>
13 : #include <unistd.h>
14 :
15 : #include <crm/crm.h>
16 : #include <crm/common/xml.h>
17 : #include <crm/services.h>
18 : #include <crm/common/mainloop.h>
19 : #include <crm/common/alerts_internal.h>
20 : #include <crm/lrmd_internal.h>
21 :
22 : #include <crm/pengine/status.h>
23 : #include <crm/cib.h>
24 : #include <crm/lrmd.h>
25 :
26 : static lrmd_key_value_t *
27 0 : alert_key2param(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
28 : const char *value)
29 : {
30 : const char **key;
31 :
32 0 : if (value == NULL) {
33 0 : value = "";
34 : }
35 0 : for (key = pcmk__alert_keys[name]; *key; key++) {
36 0 : crm_trace("Setting alert key %s = '%s'", *key, value);
37 0 : head = lrmd_key_value_add(head, *key, value);
38 : }
39 0 : return head;
40 : }
41 :
42 : static lrmd_key_value_t *
43 0 : alert_key2param_int(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
44 : int value)
45 : {
46 0 : char *value_s = pcmk__itoa(value);
47 :
48 0 : head = alert_key2param(head, name, value_s);
49 0 : free(value_s);
50 0 : return head;
51 : }
52 :
53 : static lrmd_key_value_t *
54 0 : alert_key2param_ms(lrmd_key_value_t *head, enum pcmk__alert_keys_e name,
55 : guint value)
56 : {
57 0 : char *value_s = crm_strdup_printf("%u", value);
58 :
59 0 : head = alert_key2param(head, name, value_s);
60 0 : free(value_s);
61 0 : return head;
62 : }
63 :
64 : static void
65 0 : set_ev_kv(gpointer key, gpointer value, gpointer user_data)
66 : {
67 0 : lrmd_key_value_t **head = (lrmd_key_value_t **) user_data;
68 :
69 0 : if (value) {
70 0 : crm_trace("Setting environment variable %s='%s'",
71 : (char*)key, (char*)value);
72 0 : *head = lrmd_key_value_add(*head, key, value);
73 : }
74 0 : }
75 :
76 : static lrmd_key_value_t *
77 0 : alert_envvar2params(lrmd_key_value_t *head, const pcmk__alert_t *entry)
78 : {
79 0 : if (entry->envvars) {
80 0 : g_hash_table_foreach(entry->envvars, set_ev_kv, &head);
81 : }
82 0 : return head;
83 : }
84 :
85 : /*
86 : * We could use g_strv_contains() instead of this function,
87 : * but that has only been available since glib 2.43.2.
88 : */
89 : static gboolean
90 0 : is_target_alert(char **list, const char *value)
91 : {
92 0 : int target_list_num = 0;
93 0 : gboolean rc = FALSE;
94 :
95 0 : CRM_CHECK(value != NULL, return FALSE);
96 :
97 0 : if (list == NULL) {
98 0 : return TRUE;
99 : }
100 :
101 0 : target_list_num = g_strv_length(list);
102 :
103 0 : for (int cnt = 0; cnt < target_list_num; cnt++) {
104 0 : if (strcmp(list[cnt], value) == 0) {
105 0 : rc = TRUE;
106 0 : break;
107 : }
108 : }
109 0 : return rc;
110 : }
111 :
112 : /*!
113 : * \internal
114 : * \brief Execute alert agents for an event
115 : *
116 : * \param[in,out] lrmd Executor connection to use
117 : * \param[in] alert_list Alerts to execute
118 : * \param[in] kind Type of event that is being alerted for
119 : * \param[in] attr_name If pcmk__alert_attribute, the attribute name
120 : * \param[in,out] params Environment variables to pass to agents
121 : *
122 : * \retval pcmk_ok on success
123 : * \retval -1 if some alerts failed
124 : * \retval -2 if all alerts failed
125 : */
126 : static int
127 0 : exec_alert_list(lrmd_t *lrmd, const GList *alert_list,
128 : enum pcmk__alert_flags kind, const char *attr_name,
129 : lrmd_key_value_t *params)
130 : {
131 0 : bool any_success = FALSE, any_failure = FALSE;
132 0 : const char *kind_s = pcmk__alert_flag2text(kind);
133 0 : pcmk__time_hr_t *now = NULL;
134 : char timestamp_epoch[20];
135 : char timestamp_usec[7];
136 0 : time_t epoch = 0;
137 :
138 0 : params = alert_key2param(params, PCMK__alert_key_kind, kind_s);
139 0 : params = alert_key2param(params, PCMK__alert_key_version,
140 : PACEMAKER_VERSION);
141 :
142 0 : for (const GList *iter = alert_list;
143 0 : iter != NULL; iter = g_list_next(iter)) {
144 0 : const pcmk__alert_t *entry = (pcmk__alert_t *) (iter->data);
145 0 : lrmd_key_value_t *copy_params = NULL;
146 0 : lrmd_key_value_t *head = NULL;
147 : int rc;
148 :
149 0 : if (!pcmk_is_set(entry->flags, kind)) {
150 0 : crm_trace("Filtering unwanted %s alert to %s via %s",
151 : kind_s, entry->recipient, entry->id);
152 0 : continue;
153 : }
154 :
155 0 : if ((kind == pcmk__alert_attribute)
156 0 : && !is_target_alert(entry->select_attribute_name, attr_name)) {
157 :
158 0 : crm_trace("Filtering unwanted attribute '%s' alert to %s via %s",
159 : attr_name, entry->recipient, entry->id);
160 0 : continue;
161 : }
162 :
163 0 : if (now == NULL) {
164 0 : now = pcmk__time_hr_now(&epoch);
165 : }
166 0 : crm_info("Sending %s alert via %s to %s",
167 : kind_s, entry->id, entry->recipient);
168 :
169 : /* Make a copy of the parameters, because each alert will be unique */
170 0 : for (head = params; head != NULL; head = head->next) {
171 0 : copy_params = lrmd_key_value_add(copy_params, head->key, head->value);
172 : }
173 :
174 0 : copy_params = alert_key2param(copy_params, PCMK__alert_key_recipient,
175 0 : entry->recipient);
176 :
177 0 : if (now) {
178 0 : char *timestamp = pcmk__time_format_hr(entry->tstamp_format, now);
179 :
180 0 : if (timestamp) {
181 0 : copy_params = alert_key2param(copy_params,
182 : PCMK__alert_key_timestamp,
183 : timestamp);
184 0 : free(timestamp);
185 : }
186 :
187 0 : snprintf(timestamp_epoch, sizeof(timestamp_epoch), "%lld",
188 : (long long) epoch);
189 0 : copy_params = alert_key2param(copy_params,
190 : PCMK__alert_key_timestamp_epoch,
191 : timestamp_epoch);
192 0 : snprintf(timestamp_usec, sizeof(timestamp_usec), "%06d", now->useconds);
193 0 : copy_params = alert_key2param(copy_params,
194 : PCMK__alert_key_timestamp_usec,
195 : timestamp_usec);
196 : }
197 :
198 0 : copy_params = alert_envvar2params(copy_params, entry);
199 :
200 0 : rc = lrmd->cmds->exec_alert(lrmd, entry->id, entry->path,
201 0 : entry->timeout, copy_params);
202 0 : if (rc < 0) {
203 0 : crm_err("Could not execute alert %s: %s " CRM_XS " rc=%d",
204 : entry->id, pcmk_strerror(rc), rc);
205 0 : any_failure = TRUE;
206 : } else {
207 0 : any_success = TRUE;
208 : }
209 : }
210 :
211 0 : if (now) {
212 0 : free(now);
213 : }
214 :
215 0 : if (any_failure) {
216 0 : return (any_success? -1 : -2);
217 : }
218 0 : return pcmk_ok;
219 : }
220 :
221 : /*!
222 : * \internal
223 : * \brief Send an alert for a node attribute change
224 : *
225 : * \param[in,out] lrmd Executor connection to use
226 : * \param[in] alert_list List of alert agents to execute
227 : * \param[in] node Name of node with attribute change
228 : * \param[in] nodeid Node ID of node with attribute change
229 : * \param[in] attr_name Name of attribute that changed
230 : * \param[in] attr_value New value of attribute that changed
231 : *
232 : * \retval pcmk_ok on success
233 : * \retval -1 if some alert agents failed
234 : * \retval -2 if all alert agents failed
235 : */
236 : int
237 0 : lrmd_send_attribute_alert(lrmd_t *lrmd, const GList *alert_list,
238 : const char *node, uint32_t nodeid,
239 : const char *attr_name, const char *attr_value)
240 : {
241 0 : int rc = pcmk_ok;
242 0 : lrmd_key_value_t *params = NULL;
243 :
244 0 : if (lrmd == NULL) {
245 0 : return -2;
246 : }
247 :
248 0 : params = alert_key2param(params, PCMK__alert_key_node, node);
249 0 : params = alert_key2param_int(params, PCMK__alert_key_nodeid, nodeid);
250 0 : params = alert_key2param(params, PCMK__alert_key_attribute_name, attr_name);
251 0 : params = alert_key2param(params, PCMK__alert_key_attribute_value,
252 : attr_value);
253 :
254 0 : rc = exec_alert_list(lrmd, alert_list, pcmk__alert_attribute, attr_name,
255 : params);
256 0 : lrmd_key_value_freeall(params);
257 0 : return rc;
258 : }
259 :
260 : /*!
261 : * \internal
262 : * \brief Send an alert for a node membership event
263 : *
264 : * \param[in,out] lrmd Executor connection to use
265 : * \param[in] alert_list List of alert agents to execute
266 : * \param[in] node Name of node with change
267 : * \param[in] nodeid Node ID of node with change
268 : * \param[in] state New state of node with change
269 : *
270 : * \retval pcmk_ok on success
271 : * \retval -1 if some alert agents failed
272 : * \retval -2 if all alert agents failed
273 : */
274 : int
275 0 : lrmd_send_node_alert(lrmd_t *lrmd, const GList *alert_list,
276 : const char *node, uint32_t nodeid, const char *state)
277 : {
278 0 : int rc = pcmk_ok;
279 0 : lrmd_key_value_t *params = NULL;
280 :
281 0 : if (lrmd == NULL) {
282 0 : return -2;
283 : }
284 :
285 0 : params = alert_key2param(params, PCMK__alert_key_node, node);
286 0 : params = alert_key2param(params, PCMK__alert_key_desc, state);
287 0 : params = alert_key2param_int(params, PCMK__alert_key_nodeid, nodeid);
288 :
289 0 : rc = exec_alert_list(lrmd, alert_list, pcmk__alert_node, NULL, params);
290 0 : lrmd_key_value_freeall(params);
291 0 : return rc;
292 : }
293 :
294 : /*!
295 : * \internal
296 : * \brief Send an alert for a fencing event
297 : *
298 : * \param[in,out] lrmd Executor connection to use
299 : * \param[in] alert_list List of alert agents to execute
300 : * \param[in] target Name of fence target node
301 : * \param[in] task Type of fencing event that occurred
302 : * \param[in] desc Readable description of event
303 : * \param[in] op_rc Result of fence action
304 : *
305 : * \retval pcmk_ok on success
306 : * \retval -1 if some alert agents failed
307 : * \retval -2 if all alert agents failed
308 : */
309 : int
310 0 : lrmd_send_fencing_alert(lrmd_t *lrmd, const GList *alert_list,
311 : const char *target, const char *task, const char *desc,
312 : int op_rc)
313 : {
314 0 : int rc = pcmk_ok;
315 0 : lrmd_key_value_t *params = NULL;
316 :
317 0 : if (lrmd == NULL) {
318 0 : return -2;
319 : }
320 :
321 0 : params = alert_key2param(params, PCMK__alert_key_node, target);
322 0 : params = alert_key2param(params, PCMK__alert_key_task, task);
323 0 : params = alert_key2param(params, PCMK__alert_key_desc, desc);
324 0 : params = alert_key2param_int(params, PCMK__alert_key_rc, op_rc);
325 :
326 0 : rc = exec_alert_list(lrmd, alert_list, pcmk__alert_fencing, NULL, params);
327 0 : lrmd_key_value_freeall(params);
328 0 : return rc;
329 : }
330 :
331 : /*!
332 : * \internal
333 : * \brief Send an alert for a resource operation
334 : *
335 : * \param[in,out] lrmd Executor connection to use
336 : * \param[in] alert_list List of alert agents to execute
337 : * \param[in] node Name of node that executed operation
338 : * \param[in] op Resource operation
339 : *
340 : * \retval pcmk_ok on success
341 : * \retval -1 if some alert agents failed
342 : * \retval -2 if all alert agents failed
343 : */
344 : int
345 0 : lrmd_send_resource_alert(lrmd_t *lrmd, const GList *alert_list,
346 : const char *node, const lrmd_event_data_t *op)
347 : {
348 0 : int rc = pcmk_ok;
349 0 : int target_rc = pcmk_ok;
350 0 : lrmd_key_value_t *params = NULL;
351 :
352 0 : if (lrmd == NULL) {
353 0 : return -2;
354 : }
355 :
356 0 : target_rc = rsc_op_expected_rc(op);
357 0 : if ((op->interval_ms == 0) && (target_rc == op->rc)
358 0 : && pcmk__str_eq(op->op_type, PCMK_ACTION_MONITOR, pcmk__str_casei)) {
359 :
360 : /* Don't send alerts for probes with the expected result. Leave it up to
361 : * the agent whether to alert for 'failed' probes. (Even if we find a
362 : * resource running, it was probably because someone did a clean-up of
363 : * the status section.)
364 : */
365 0 : return pcmk_ok;
366 : }
367 :
368 0 : params = alert_key2param(params, PCMK__alert_key_node, node);
369 0 : params = alert_key2param(params, PCMK__alert_key_rsc, op->rsc_id);
370 0 : params = alert_key2param(params, PCMK__alert_key_task, op->op_type);
371 0 : params = alert_key2param_ms(params, PCMK__alert_key_interval,
372 0 : op->interval_ms);
373 0 : params = alert_key2param_int(params, PCMK__alert_key_target_rc, target_rc);
374 0 : params = alert_key2param_int(params, PCMK__alert_key_status, op->op_status);
375 0 : params = alert_key2param_int(params, PCMK__alert_key_rc, op->rc);
376 :
377 : /* Reoccurring operations do not set exec_time, so on timeout, set it
378 : * to the operation timeout since that's closer to the actual value.
379 : */
380 0 : if ((op->op_status == PCMK_EXEC_TIMEOUT) && (op->exec_time == 0)) {
381 0 : params = alert_key2param_int(params, PCMK__alert_key_exec_time,
382 0 : op->timeout);
383 : } else {
384 0 : params = alert_key2param_int(params, PCMK__alert_key_exec_time,
385 0 : op->exec_time);
386 : }
387 :
388 0 : if (op->op_status == PCMK_EXEC_DONE) {
389 0 : params = alert_key2param(params, PCMK__alert_key_desc,
390 0 : services_ocf_exitcode_str(op->rc));
391 : } else {
392 0 : params = alert_key2param(params, PCMK__alert_key_desc,
393 0 : pcmk_exec_status_str(op->op_status));
394 : }
395 :
396 0 : rc = exec_alert_list(lrmd, alert_list, pcmk__alert_resource, NULL, params);
397 0 : lrmd_key_value_freeall(params);
398 0 : return rc;
399 : }
|