/src/pacemaker/lib/common/actions.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2004-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 <stdio.h> |
13 | | #include <string.h> |
14 | | #include <stdlib.h> |
15 | | #include <sys/types.h> |
16 | | #include <ctype.h> |
17 | | |
18 | | #include <crm/crm.h> |
19 | | #include <crm/lrmd.h> |
20 | | #include <crm/common/xml.h> |
21 | | #include <crm/common/xml_internal.h> |
22 | | #include <crm/common/util.h> |
23 | | #include <crm/common/scheduler.h> |
24 | | |
25 | | /*! |
26 | | * \internal |
27 | | * \brief Get string equivalent of an action type |
28 | | * |
29 | | * \param[in] action Action type |
30 | | * |
31 | | * \return Static string describing \p action |
32 | | */ |
33 | | const char * |
34 | | pcmk__action_text(enum pcmk__action_type action) |
35 | 0 | { |
36 | 0 | switch (action) { |
37 | 0 | case pcmk__action_stop: |
38 | 0 | return PCMK_ACTION_STOP; |
39 | | |
40 | 0 | case pcmk__action_stopped: |
41 | 0 | return PCMK_ACTION_STOPPED; |
42 | | |
43 | 0 | case pcmk__action_start: |
44 | 0 | return PCMK_ACTION_START; |
45 | | |
46 | 0 | case pcmk__action_started: |
47 | 0 | return PCMK_ACTION_RUNNING; |
48 | | |
49 | 0 | case pcmk__action_shutdown: |
50 | 0 | return PCMK_ACTION_DO_SHUTDOWN; |
51 | | |
52 | 0 | case pcmk__action_fence: |
53 | 0 | return PCMK_ACTION_STONITH; |
54 | | |
55 | 0 | case pcmk__action_monitor: |
56 | 0 | return PCMK_ACTION_MONITOR; |
57 | | |
58 | 0 | case pcmk__action_notify: |
59 | 0 | return PCMK_ACTION_NOTIFY; |
60 | | |
61 | 0 | case pcmk__action_notified: |
62 | 0 | return PCMK_ACTION_NOTIFIED; |
63 | | |
64 | 0 | case pcmk__action_promote: |
65 | 0 | return PCMK_ACTION_PROMOTE; |
66 | | |
67 | 0 | case pcmk__action_promoted: |
68 | 0 | return PCMK_ACTION_PROMOTED; |
69 | | |
70 | 0 | case pcmk__action_demote: |
71 | 0 | return PCMK_ACTION_DEMOTE; |
72 | | |
73 | 0 | case pcmk__action_demoted: |
74 | 0 | return PCMK_ACTION_DEMOTED; |
75 | | |
76 | 0 | default: // pcmk__action_unspecified or invalid |
77 | 0 | return "no_action"; |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | /*! |
82 | | * \internal |
83 | | * \brief Parse an action type from an action name |
84 | | * |
85 | | * \param[in] action_name Action name |
86 | | * |
87 | | * \return Action type corresponding to \p action_name |
88 | | */ |
89 | | enum pcmk__action_type |
90 | | pcmk__parse_action(const char *action_name) |
91 | 0 | { |
92 | 0 | if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) { |
93 | 0 | return pcmk__action_stop; |
94 | |
|
95 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOPPED, pcmk__str_none)) { |
96 | 0 | return pcmk__action_stopped; |
97 | |
|
98 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) { |
99 | 0 | return pcmk__action_start; |
100 | |
|
101 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_RUNNING, pcmk__str_none)) { |
102 | 0 | return pcmk__action_started; |
103 | |
|
104 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_DO_SHUTDOWN, |
105 | 0 | pcmk__str_none)) { |
106 | 0 | return pcmk__action_shutdown; |
107 | |
|
108 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_STONITH, pcmk__str_none)) { |
109 | 0 | return pcmk__action_fence; |
110 | |
|
111 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_MONITOR, pcmk__str_none)) { |
112 | 0 | return pcmk__action_monitor; |
113 | |
|
114 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_NOTIFY, pcmk__str_none)) { |
115 | 0 | return pcmk__action_notify; |
116 | |
|
117 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_NOTIFIED, |
118 | 0 | pcmk__str_none)) { |
119 | 0 | return pcmk__action_notified; |
120 | |
|
121 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) { |
122 | 0 | return pcmk__action_promote; |
123 | |
|
124 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)) { |
125 | 0 | return pcmk__action_demote; |
126 | |
|
127 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTED, |
128 | 0 | pcmk__str_none)) { |
129 | 0 | return pcmk__action_promoted; |
130 | |
|
131 | 0 | } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTED, pcmk__str_none)) { |
132 | 0 | return pcmk__action_demoted; |
133 | 0 | } |
134 | 0 | return pcmk__action_unspecified; |
135 | 0 | } |
136 | | |
137 | | /*! |
138 | | * \internal |
139 | | * \brief Get string equivalent of a failure handling type |
140 | | * |
141 | | * \param[in] on_fail Failure handling type |
142 | | * |
143 | | * \return Static string describing \p on_fail |
144 | | */ |
145 | | const char * |
146 | | pcmk__on_fail_text(enum pcmk__on_fail on_fail) |
147 | 0 | { |
148 | 0 | switch (on_fail) { |
149 | 0 | case pcmk__on_fail_ignore: |
150 | 0 | return "ignore"; |
151 | | |
152 | 0 | case pcmk__on_fail_demote: |
153 | 0 | return "demote"; |
154 | | |
155 | 0 | case pcmk__on_fail_block: |
156 | 0 | return "block"; |
157 | | |
158 | 0 | case pcmk__on_fail_restart: |
159 | 0 | return "recover"; |
160 | | |
161 | 0 | case pcmk__on_fail_ban: |
162 | 0 | return "migrate"; |
163 | | |
164 | 0 | case pcmk__on_fail_stop: |
165 | 0 | return "stop"; |
166 | | |
167 | 0 | case pcmk__on_fail_fence_node: |
168 | 0 | return "fence"; |
169 | | |
170 | 0 | case pcmk__on_fail_standby_node: |
171 | 0 | return "standby"; |
172 | | |
173 | 0 | case pcmk__on_fail_restart_container: |
174 | 0 | return "restart-container"; |
175 | | |
176 | 0 | case pcmk__on_fail_reset_remote: |
177 | 0 | return "reset-remote"; |
178 | 0 | } |
179 | 0 | return "<unknown>"; |
180 | 0 | } |
181 | | |
182 | | /*! |
183 | | * \brief Generate an operation key (RESOURCE_ACTION_INTERVAL) |
184 | | * |
185 | | * \param[in] rsc_id ID of resource being operated on |
186 | | * \param[in] op_type Operation name |
187 | | * \param[in] interval_ms Operation interval |
188 | | * |
189 | | * \return Newly allocated memory containing operation key as string |
190 | | * |
191 | | * \note This function asserts on errors, so it will never return NULL. |
192 | | * The caller is responsible for freeing the result with free(). |
193 | | */ |
194 | | char * |
195 | | pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms) |
196 | 0 | { |
197 | 0 | pcmk__assert((rsc_id != NULL) && (op_type != NULL)); |
198 | 0 | return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms); |
199 | 0 | } |
200 | | |
201 | | static inline gboolean |
202 | | convert_interval(const char *s, guint *interval_ms) |
203 | 0 | { |
204 | 0 | unsigned long l; |
205 | |
|
206 | 0 | errno = 0; |
207 | 0 | l = strtoul(s, NULL, 10); |
208 | |
|
209 | 0 | if (errno != 0) { |
210 | 0 | return FALSE; |
211 | 0 | } |
212 | | |
213 | 0 | *interval_ms = (guint) l; |
214 | 0 | return TRUE; |
215 | 0 | } |
216 | | |
217 | | /*! |
218 | | * \internal |
219 | | * \brief Check for underbar-separated substring match |
220 | | * |
221 | | * \param[in] key Overall string being checked |
222 | | * \param[in] position Match before underbar at this \p key index |
223 | | * \param[in] matches Substrings to match (may contain underbars) |
224 | | * |
225 | | * \return \p key index of underbar before any matching substring, |
226 | | * or 0 if none |
227 | | */ |
228 | | static size_t |
229 | | match_before(const char *key, size_t position, const char **matches) |
230 | 0 | { |
231 | 0 | for (int i = 0; matches[i] != NULL; ++i) { |
232 | 0 | const size_t match_len = strlen(matches[i]); |
233 | | |
234 | | // Must have at least X_MATCH before position |
235 | 0 | if (position > (match_len + 1)) { |
236 | 0 | const size_t possible = position - match_len - 1; |
237 | |
|
238 | 0 | if ((key[possible] == '_') |
239 | 0 | && (strncmp(key + possible + 1, matches[i], match_len) == 0)) { |
240 | 0 | return possible; |
241 | 0 | } |
242 | 0 | } |
243 | 0 | } |
244 | 0 | return 0; |
245 | 0 | } |
246 | | |
247 | | gboolean |
248 | | parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms) |
249 | 0 | { |
250 | 0 | guint local_interval_ms = 0; |
251 | 0 | const size_t key_len = (key == NULL)? 0 : strlen(key); |
252 | | |
253 | | // Operation keys must be formatted as RSC_ACTION_INTERVAL |
254 | 0 | size_t action_underbar = 0; // Index in key of underbar before ACTION |
255 | 0 | size_t interval_underbar = 0; // Index in key of underbar before INTERVAL |
256 | 0 | size_t possible = 0; |
257 | | |
258 | | /* Underbar was a poor choice of separator since both RSC and ACTION can |
259 | | * contain underbars. Here, list action names and name prefixes that can. |
260 | | */ |
261 | 0 | const char *actions_with_underbars[] = { |
262 | 0 | PCMK_ACTION_MIGRATE_FROM, |
263 | 0 | PCMK_ACTION_MIGRATE_TO, |
264 | 0 | NULL |
265 | 0 | }; |
266 | 0 | const char *action_prefixes_with_underbars[] = { |
267 | 0 | "pre_" PCMK_ACTION_NOTIFY, |
268 | 0 | "post_" PCMK_ACTION_NOTIFY, |
269 | 0 | "confirmed-pre_" PCMK_ACTION_NOTIFY, |
270 | 0 | "confirmed-post_" PCMK_ACTION_NOTIFY, |
271 | 0 | NULL, |
272 | 0 | }; |
273 | | |
274 | | // Initialize output variables in case of early return |
275 | 0 | if (rsc_id) { |
276 | 0 | *rsc_id = NULL; |
277 | 0 | } |
278 | 0 | if (op_type) { |
279 | 0 | *op_type = NULL; |
280 | 0 | } |
281 | 0 | if (interval_ms) { |
282 | 0 | *interval_ms = 0; |
283 | 0 | } |
284 | | |
285 | | // RSC_ACTION_INTERVAL implies a minimum of 5 characters |
286 | 0 | if (key_len < 5) { |
287 | 0 | return FALSE; |
288 | 0 | } |
289 | | |
290 | | // Find, parse, and validate interval |
291 | 0 | interval_underbar = key_len - 2; |
292 | 0 | while ((interval_underbar > 2) && (key[interval_underbar] != '_')) { |
293 | 0 | --interval_underbar; |
294 | 0 | } |
295 | 0 | if ((interval_underbar == 2) |
296 | 0 | || !convert_interval(key + interval_underbar + 1, &local_interval_ms)) { |
297 | 0 | return FALSE; |
298 | 0 | } |
299 | | |
300 | | // Find the base (OCF) action name, disregarding prefixes |
301 | 0 | action_underbar = match_before(key, interval_underbar, |
302 | 0 | actions_with_underbars); |
303 | 0 | if (action_underbar == 0) { |
304 | 0 | action_underbar = interval_underbar - 2; |
305 | 0 | while ((action_underbar > 0) && (key[action_underbar] != '_')) { |
306 | 0 | --action_underbar; |
307 | 0 | } |
308 | 0 | if (action_underbar == 0) { |
309 | 0 | return FALSE; |
310 | 0 | } |
311 | 0 | } |
312 | 0 | possible = match_before(key, action_underbar, |
313 | 0 | action_prefixes_with_underbars); |
314 | 0 | if (possible != 0) { |
315 | 0 | action_underbar = possible; |
316 | 0 | } |
317 | | |
318 | | // Set output variables |
319 | 0 | if (rsc_id != NULL) { |
320 | 0 | *rsc_id = strndup(key, action_underbar); |
321 | 0 | pcmk__mem_assert(*rsc_id); |
322 | 0 | } |
323 | 0 | if (op_type != NULL) { |
324 | 0 | *op_type = strndup(key + action_underbar + 1, |
325 | 0 | interval_underbar - action_underbar - 1); |
326 | 0 | pcmk__mem_assert(*op_type); |
327 | 0 | } |
328 | 0 | if (interval_ms != NULL) { |
329 | 0 | *interval_ms = local_interval_ms; |
330 | 0 | } |
331 | 0 | return TRUE; |
332 | 0 | } |
333 | | |
334 | | char * |
335 | | pcmk__notify_key(const char *rsc_id, const char *notify_type, |
336 | | const char *op_type) |
337 | 0 | { |
338 | 0 | CRM_CHECK(rsc_id != NULL, return NULL); |
339 | 0 | CRM_CHECK(op_type != NULL, return NULL); |
340 | 0 | CRM_CHECK(notify_type != NULL, return NULL); |
341 | 0 | return crm_strdup_printf("%s_%s_notify_%s_0", |
342 | 0 | rsc_id, notify_type, op_type); |
343 | 0 | } |
344 | | |
345 | | /*! |
346 | | * \brief Parse a transition magic string into its constituent parts |
347 | | * |
348 | | * \param[in] magic Magic string to parse (must be non-NULL) |
349 | | * \param[out] uuid If non-NULL, where to store copy of parsed UUID |
350 | | * \param[out] transition_id If non-NULL, where to store parsed transition ID |
351 | | * \param[out] action_id If non-NULL, where to store parsed action ID |
352 | | * \param[out] op_status If non-NULL, where to store parsed result status |
353 | | * \param[out] op_rc If non-NULL, where to store parsed actual rc |
354 | | * \param[out] target_rc If non-NULL, where to stored parsed target rc |
355 | | * |
356 | | * \return TRUE if key was valid, FALSE otherwise |
357 | | * \note If uuid is supplied and this returns TRUE, the caller is responsible |
358 | | * for freeing the memory for *uuid using free(). |
359 | | */ |
360 | | gboolean |
361 | | decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id, |
362 | | int *op_status, int *op_rc, int *target_rc) |
363 | 0 | { |
364 | 0 | int res = 0; |
365 | 0 | char *key = NULL; |
366 | 0 | gboolean result = TRUE; |
367 | 0 | int local_op_status = -1; |
368 | 0 | int local_op_rc = -1; |
369 | |
|
370 | 0 | CRM_CHECK(magic != NULL, return FALSE); |
371 | | |
372 | 0 | #ifdef HAVE_SSCANF_M |
373 | 0 | res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key); |
374 | | #else |
375 | | // magic must have >=4 other characters |
376 | | key = pcmk__assert_alloc(1, strlen(magic) - 3); |
377 | | res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key); |
378 | | #endif |
379 | 0 | if (res == EOF) { |
380 | 0 | crm_err("Could not decode transition information '%s': %s", |
381 | 0 | magic, pcmk_rc_str(errno)); |
382 | 0 | result = FALSE; |
383 | 0 | } else if (res < 3) { |
384 | 0 | crm_warn("Transition information '%s' incomplete (%d of 3 expected items)", |
385 | 0 | magic, res); |
386 | 0 | result = FALSE; |
387 | 0 | } else { |
388 | 0 | if (op_status) { |
389 | 0 | *op_status = local_op_status; |
390 | 0 | } |
391 | 0 | if (op_rc) { |
392 | 0 | *op_rc = local_op_rc; |
393 | 0 | } |
394 | 0 | result = decode_transition_key(key, uuid, transition_id, action_id, |
395 | 0 | target_rc); |
396 | 0 | } |
397 | 0 | free(key); |
398 | 0 | return result; |
399 | 0 | } |
400 | | |
401 | | char * |
402 | | pcmk__transition_key(int transition_id, int action_id, int target_rc, |
403 | | const char *node) |
404 | 0 | { |
405 | 0 | CRM_CHECK(node != NULL, return NULL); |
406 | 0 | return crm_strdup_printf("%d:%d:%d:%-*s", |
407 | 0 | action_id, transition_id, target_rc, 36, node); |
408 | 0 | } |
409 | | |
410 | | /*! |
411 | | * \brief Parse a transition key into its constituent parts |
412 | | * |
413 | | * \param[in] key Transition key to parse (must be non-NULL) |
414 | | * \param[out] uuid If non-NULL, where to store copy of parsed UUID |
415 | | * \param[out] transition_id If non-NULL, where to store parsed transition ID |
416 | | * \param[out] action_id If non-NULL, where to store parsed action ID |
417 | | * \param[out] target_rc If non-NULL, where to stored parsed target rc |
418 | | * |
419 | | * \return TRUE if key was valid, FALSE otherwise |
420 | | * \note If uuid is supplied and this returns TRUE, the caller is responsible |
421 | | * for freeing the memory for *uuid using free(). |
422 | | */ |
423 | | gboolean |
424 | | decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id, |
425 | | int *target_rc) |
426 | 0 | { |
427 | 0 | int local_transition_id = -1; |
428 | 0 | int local_action_id = -1; |
429 | 0 | int local_target_rc = -1; |
430 | 0 | char local_uuid[37] = { '\0' }; |
431 | | |
432 | | // Initialize any supplied output arguments |
433 | 0 | if (uuid) { |
434 | 0 | *uuid = NULL; |
435 | 0 | } |
436 | 0 | if (transition_id) { |
437 | 0 | *transition_id = -1; |
438 | 0 | } |
439 | 0 | if (action_id) { |
440 | 0 | *action_id = -1; |
441 | 0 | } |
442 | 0 | if (target_rc) { |
443 | 0 | *target_rc = -1; |
444 | 0 | } |
445 | |
|
446 | 0 | CRM_CHECK(key != NULL, return FALSE); |
447 | 0 | if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id, |
448 | 0 | &local_target_rc, local_uuid) != 4) { |
449 | 0 | crm_err("Invalid transition key '%s'", key); |
450 | 0 | return FALSE; |
451 | 0 | } |
452 | 0 | if (strlen(local_uuid) != 36) { |
453 | 0 | crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key); |
454 | 0 | } |
455 | 0 | if (uuid) { |
456 | 0 | *uuid = pcmk__str_copy(local_uuid); |
457 | 0 | } |
458 | 0 | if (transition_id) { |
459 | 0 | *transition_id = local_transition_id; |
460 | 0 | } |
461 | 0 | if (action_id) { |
462 | 0 | *action_id = local_action_id; |
463 | 0 | } |
464 | 0 | if (target_rc) { |
465 | 0 | *target_rc = local_target_rc; |
466 | 0 | } |
467 | 0 | return TRUE; |
468 | 0 | } |
469 | | |
470 | | int |
471 | | rsc_op_expected_rc(const lrmd_event_data_t *op) |
472 | 0 | { |
473 | 0 | int rc = 0; |
474 | |
|
475 | 0 | if (op && op->user_data) { |
476 | 0 | decode_transition_key(op->user_data, NULL, NULL, NULL, &rc); |
477 | 0 | } |
478 | 0 | return rc; |
479 | 0 | } |
480 | | |
481 | | gboolean |
482 | | did_rsc_op_fail(lrmd_event_data_t * op, int target_rc) |
483 | 0 | { |
484 | 0 | switch (op->op_status) { |
485 | 0 | case PCMK_EXEC_CANCELLED: |
486 | 0 | case PCMK_EXEC_PENDING: |
487 | 0 | return FALSE; |
488 | | |
489 | 0 | case PCMK_EXEC_NOT_SUPPORTED: |
490 | 0 | case PCMK_EXEC_TIMEOUT: |
491 | 0 | case PCMK_EXEC_ERROR: |
492 | 0 | case PCMK_EXEC_NOT_CONNECTED: |
493 | 0 | case PCMK_EXEC_NO_FENCE_DEVICE: |
494 | 0 | case PCMK_EXEC_NO_SECRETS: |
495 | 0 | case PCMK_EXEC_INVALID: |
496 | 0 | return TRUE; |
497 | | |
498 | 0 | default: |
499 | 0 | if (target_rc != op->rc) { |
500 | 0 | return TRUE; |
501 | 0 | } |
502 | 0 | } |
503 | | |
504 | 0 | return FALSE; |
505 | 0 | } |
506 | | |
507 | | /*! |
508 | | * \brief Create a CIB XML element for an operation |
509 | | * |
510 | | * \param[in,out] parent If not NULL, make new XML node a child of this |
511 | | * \param[in] prefix Generate an ID using this prefix |
512 | | * \param[in] task Operation task to set |
513 | | * \param[in] interval_spec Operation interval to set |
514 | | * \param[in] timeout If not NULL, operation timeout to set |
515 | | * |
516 | | * \return New XML object on success, NULL otherwise |
517 | | */ |
518 | | xmlNode * |
519 | | crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task, |
520 | | const char *interval_spec, const char *timeout) |
521 | 0 | { |
522 | 0 | xmlNode *xml_op; |
523 | |
|
524 | 0 | CRM_CHECK(prefix && task && interval_spec, return NULL); |
525 | | |
526 | 0 | xml_op = pcmk__xe_create(parent, PCMK_XE_OP); |
527 | 0 | pcmk__xe_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec); |
528 | 0 | crm_xml_add(xml_op, PCMK_META_INTERVAL, interval_spec); |
529 | 0 | crm_xml_add(xml_op, PCMK_XA_NAME, task); |
530 | 0 | if (timeout) { |
531 | 0 | crm_xml_add(xml_op, PCMK_META_TIMEOUT, timeout); |
532 | 0 | } |
533 | 0 | return xml_op; |
534 | 0 | } |
535 | | |
536 | | /*! |
537 | | * \brief Check whether an operation requires resource agent meta-data |
538 | | * |
539 | | * \param[in] rsc_class Resource agent class (or NULL to skip class check) |
540 | | * \param[in] op Operation action (or NULL to skip op check) |
541 | | * |
542 | | * \return true if operation needs meta-data, false otherwise |
543 | | * \note At least one of rsc_class and op must be specified. |
544 | | */ |
545 | | bool |
546 | | crm_op_needs_metadata(const char *rsc_class, const char *op) |
547 | 0 | { |
548 | | /* Agent metadata is used to determine whether an agent reload is possible, |
549 | | * so if this op is not relevant to that feature, we don't need metadata. |
550 | | */ |
551 | |
|
552 | 0 | CRM_CHECK((rsc_class != NULL) || (op != NULL), return false); |
553 | | |
554 | 0 | if ((rsc_class != NULL) |
555 | 0 | && !pcmk_is_set(pcmk_get_ra_caps(rsc_class), pcmk_ra_cap_params)) { |
556 | | // Metadata is needed only for resource classes that use parameters |
557 | 0 | return false; |
558 | 0 | } |
559 | 0 | if (op == NULL) { |
560 | 0 | return true; |
561 | 0 | } |
562 | | |
563 | | // Metadata is needed only for these actions |
564 | 0 | return pcmk__str_any_of(op, PCMK_ACTION_START, PCMK_ACTION_MONITOR, |
565 | 0 | PCMK_ACTION_PROMOTE, PCMK_ACTION_DEMOTE, |
566 | 0 | PCMK_ACTION_RELOAD, PCMK_ACTION_RELOAD_AGENT, |
567 | 0 | PCMK_ACTION_MIGRATE_TO, PCMK_ACTION_MIGRATE_FROM, |
568 | 0 | PCMK_ACTION_NOTIFY, NULL); |
569 | 0 | } |
570 | | |
571 | | /*! |
572 | | * \internal |
573 | | * \brief Check whether an action name is for a fencing action |
574 | | * |
575 | | * \param[in] action Action name to check |
576 | | * |
577 | | * \return \c true if \p action is \c PCMK_ACTION_OFF or \c PCMK_ACTION_REBOOT, |
578 | | * or \c false otherwise |
579 | | */ |
580 | | bool |
581 | | pcmk__is_fencing_action(const char *action) |
582 | 0 | { |
583 | 0 | return pcmk__str_any_of(action, PCMK_ACTION_OFF, PCMK_ACTION_REBOOT, NULL); |
584 | 0 | } |