1 /*
2  * Copyright 2004-2021 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/msg_xml.h>
13 #include <crm/common/xml.h>
14 #include <crm/common/xml_internal.h>
15 #include <crm/common/util.h>
16 
17 #include <glib.h>
18 #include <stdbool.h>
19 
20 #include <crm/pengine/rules.h>
21 #include <crm/pengine/internal.h>
22 #include "pe_status_private.h"
23 
24 extern bool pcmk__is_daemon;
25 
26 extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
27 void print_str_str(gpointer key, gpointer value, gpointer user_data);
28 gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
29 static void unpack_operation(pe_action_t * action, xmlNode * xml_obj, pe_resource_t * container,
30                              pe_working_set_t * data_set, guint interval_ms);
31 static xmlNode *find_rsc_op_entry_helper(pe_resource_t * rsc, const char *key,
32                                          gboolean include_disabled);
33 
34 #if ENABLE_VERSIONED_ATTRS
35 pe_rsc_action_details_t *
pe_rsc_action_details(pe_action_t * action)36 pe_rsc_action_details(pe_action_t *action)
37 {
38     pe_rsc_action_details_t *details;
39 
40     CRM_CHECK(action != NULL, return NULL);
41 
42     if (action->action_details == NULL) {
43         action->action_details = calloc(1, sizeof(pe_rsc_action_details_t));
44         CRM_CHECK(action->action_details != NULL, return NULL);
45     }
46 
47     details = (pe_rsc_action_details_t *) action->action_details;
48     if (details->versioned_parameters == NULL) {
49         details->versioned_parameters = create_xml_node(NULL,
50                                                         XML_TAG_OP_VER_ATTRS);
51     }
52     if (details->versioned_meta == NULL) {
53         details->versioned_meta = create_xml_node(NULL, XML_TAG_OP_VER_META);
54     }
55     return details;
56 }
57 
58 static void
pe_free_rsc_action_details(pe_action_t * action)59 pe_free_rsc_action_details(pe_action_t *action)
60 {
61     pe_rsc_action_details_t *details;
62 
63     if ((action == NULL) || (action->action_details == NULL)) {
64         return;
65     }
66 
67     details = (pe_rsc_action_details_t *) action->action_details;
68 
69     if (details->versioned_parameters) {
70         free_xml(details->versioned_parameters);
71     }
72     if (details->versioned_meta) {
73         free_xml(details->versioned_meta);
74     }
75 
76     action->action_details = NULL;
77 }
78 #endif
79 
80 /*!
81  * \internal
82  * \brief Check whether we can fence a particular node
83  *
84  * \param[in] data_set  Working set for cluster
85  * \param[in] node      Name of node to check
86  *
87  * \return true if node can be fenced, false otherwise
88  */
89 bool
pe_can_fence(pe_working_set_t * data_set,pe_node_t * node)90 pe_can_fence(pe_working_set_t *data_set, pe_node_t *node)
91 {
92     if (pe__is_guest_node(node)) {
93         /* Guest nodes are fenced by stopping their container resource. We can
94          * do that if the container's host is either online or fenceable.
95          */
96         pe_resource_t *rsc = node->details->remote_rsc->container;
97 
98         for (GList *n = rsc->running_on; n != NULL; n = n->next) {
99             pe_node_t *container_node = n->data;
100 
101             if (!container_node->details->online
102                 && !pe_can_fence(data_set, container_node)) {
103                 return false;
104             }
105         }
106         return true;
107 
108     } else if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
109         return false; /* Turned off */
110 
111     } else if (!pcmk_is_set(data_set->flags, pe_flag_have_stonith_resource)) {
112         return false; /* No devices */
113 
114     } else if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)) {
115         return true;
116 
117     } else if (data_set->no_quorum_policy == no_quorum_ignore) {
118         return true;
119 
120     } else if(node == NULL) {
121         return false;
122 
123     } else if(node->details->online) {
124         crm_notice("We can fence %s without quorum because they're in our membership", node->details->uname);
125         return true;
126     }
127 
128     crm_trace("Cannot fence %s", node->details->uname);
129     return false;
130 }
131 
132 /*!
133  * \internal
134  * \brief Copy a node object
135  *
136  * \param[in] this_node  Node object to copy
137  *
138  * \return Newly allocated shallow copy of this_node
139  * \note This function asserts on errors and is guaranteed to return non-NULL.
140  */
141 pe_node_t *
pe__copy_node(const pe_node_t * this_node)142 pe__copy_node(const pe_node_t *this_node)
143 {
144     pe_node_t *new_node = NULL;
145 
146     CRM_ASSERT(this_node != NULL);
147 
148     new_node = calloc(1, sizeof(pe_node_t));
149     CRM_ASSERT(new_node != NULL);
150 
151     new_node->rsc_discover_mode = this_node->rsc_discover_mode;
152     new_node->weight = this_node->weight;
153     new_node->fixed = this_node->fixed;
154     new_node->details = this_node->details;
155 
156     return new_node;
157 }
158 
159 /* any node in list1 or list2 and not in the other gets a score of -INFINITY */
160 void
node_list_exclude(GHashTable * hash,GList * list,gboolean merge_scores)161 node_list_exclude(GHashTable * hash, GList *list, gboolean merge_scores)
162 {
163     GHashTable *result = hash;
164     pe_node_t *other_node = NULL;
165     GList *gIter = list;
166 
167     GHashTableIter iter;
168     pe_node_t *node = NULL;
169 
170     g_hash_table_iter_init(&iter, hash);
171     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
172 
173         other_node = pe_find_node_id(list, node->details->id);
174         if (other_node == NULL) {
175             node->weight = -INFINITY;
176         } else if (merge_scores) {
177             node->weight = pe__add_scores(node->weight, other_node->weight);
178         }
179     }
180 
181     for (; gIter != NULL; gIter = gIter->next) {
182         pe_node_t *node = (pe_node_t *) gIter->data;
183 
184         other_node = pe_hash_table_lookup(result, node->details->id);
185 
186         if (other_node == NULL) {
187             pe_node_t *new_node = pe__copy_node(node);
188 
189             new_node->weight = -INFINITY;
190             g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
191         }
192     }
193 }
194 
195 /*!
196  * \internal
197  * \brief Create a node hash table from a node list
198  *
199  * \param[in] list  Node list
200  *
201  * \return Hash table equivalent of node list
202  */
203 GHashTable *
pe__node_list2table(GList * list)204 pe__node_list2table(GList *list)
205 {
206     GHashTable *result = NULL;
207 
208     result = pcmk__strkey_table(NULL, free);
209     for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
210         pe_node_t *new_node = pe__copy_node((pe_node_t *) gIter->data);
211 
212         g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
213     }
214     return result;
215 }
216 
217 gint
sort_node_uname(gconstpointer a,gconstpointer b)218 sort_node_uname(gconstpointer a, gconstpointer b)
219 {
220     return pcmk__numeric_strcasecmp(((const pe_node_t *) a)->details->uname,
221                                     ((const pe_node_t *) b)->details->uname);
222 }
223 
224 /*!
225  * \internal
226  * \brief Output node weights to stdout
227  *
228  * \param[in] rsc       Use allowed nodes for this resource
229  * \param[in] comment   Text description to prefix lines with
230  * \param[in] nodes     If rsc is not specified, use these nodes
231  */
232 static void
pe__output_node_weights(pe_resource_t * rsc,const char * comment,GHashTable * nodes,pe_working_set_t * data_set)233 pe__output_node_weights(pe_resource_t *rsc, const char *comment,
234                         GHashTable *nodes, pe_working_set_t *data_set)
235 {
236     pcmk__output_t *out = data_set->priv;
237     char score[128]; // Stack-allocated since this is called frequently
238 
239     // Sort the nodes so the output is consistent for regression tests
240     GList *list = g_list_sort(g_hash_table_get_values(nodes), sort_node_uname);
241 
242     for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
243         pe_node_t *node = (pe_node_t *) gIter->data;
244 
245         score2char_stack(node->weight, score, sizeof(score));
246         out->message(out, "node-weight", rsc, comment, node->details->uname, score);
247     }
248     g_list_free(list);
249 }
250 
251 /*!
252  * \internal
253  * \brief Log node weights at trace level
254  *
255  * \param[in] file      Caller's filename
256  * \param[in] function  Caller's function name
257  * \param[in] line      Caller's line number
258  * \param[in] rsc       Use allowed nodes for this resource
259  * \param[in] comment   Text description to prefix lines with
260  * \param[in] nodes     If rsc is not specified, use these nodes
261  */
262 static void
pe__log_node_weights(const char * file,const char * function,int line,pe_resource_t * rsc,const char * comment,GHashTable * nodes)263 pe__log_node_weights(const char *file, const char *function, int line,
264                      pe_resource_t *rsc, const char *comment, GHashTable *nodes)
265 {
266     GHashTableIter iter;
267     pe_node_t *node = NULL;
268     char score[128]; // Stack-allocated since this is called frequently
269 
270     // Don't waste time if we're not tracing at this point
271     pcmk__log_else(LOG_TRACE, return);
272 
273     g_hash_table_iter_init(&iter, nodes);
274     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
275         score2char_stack(node->weight, score, sizeof(score));
276         if (rsc) {
277             qb_log_from_external_source(function, file,
278                                         "%s: %s allocation score on %s: %s",
279                                         LOG_TRACE, line, 0,
280                                         comment, rsc->id,
281                                         node->details->uname, score);
282         } else {
283             qb_log_from_external_source(function, file, "%s: %s = %s",
284                                         LOG_TRACE, line, 0,
285                                         comment, node->details->uname,
286                                         score);
287         }
288     }
289 }
290 
291 /*!
292  * \internal
293  * \brief Log or output node weights
294  *
295  * \param[in] file      Caller's filename
296  * \param[in] function  Caller's function name
297  * \param[in] line      Caller's line number
298  * \param[in] to_log    Log if true, otherwise output
299  * \param[in] rsc       Use allowed nodes for this resource
300  * \param[in] comment   Text description to prefix lines with
301  * \param[in] nodes     Use these nodes
302  */
303 void
pe__show_node_weights_as(const char * file,const char * function,int line,bool to_log,pe_resource_t * rsc,const char * comment,GHashTable * nodes,pe_working_set_t * data_set)304 pe__show_node_weights_as(const char *file, const char *function, int line,
305                          bool to_log, pe_resource_t *rsc, const char *comment,
306                          GHashTable *nodes, pe_working_set_t *data_set)
307 {
308     if (rsc != NULL && pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
309         // Don't show allocation scores for orphans
310         return;
311     }
312     if (nodes == NULL) {
313         // Nothing to show
314         return;
315     }
316 
317     if (to_log) {
318         pe__log_node_weights(file, function, line, rsc, comment, nodes);
319     } else {
320         pe__output_node_weights(rsc, comment, nodes, data_set);
321     }
322 
323     // If this resource has children, repeat recursively for each
324     if (rsc && rsc->children) {
325         for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
326             pe_resource_t *child = (pe_resource_t *) gIter->data;
327 
328             pe__show_node_weights_as(file, function, line, to_log, child,
329                                      comment, child->allowed_nodes, data_set);
330         }
331     }
332 }
333 
334 gint
sort_rsc_index(gconstpointer a,gconstpointer b)335 sort_rsc_index(gconstpointer a, gconstpointer b)
336 {
337     const pe_resource_t *resource1 = (const pe_resource_t *)a;
338     const pe_resource_t *resource2 = (const pe_resource_t *)b;
339 
340     if (a == NULL && b == NULL) {
341         return 0;
342     }
343     if (a == NULL) {
344         return 1;
345     }
346     if (b == NULL) {
347         return -1;
348     }
349 
350     if (resource1->sort_index > resource2->sort_index) {
351         return -1;
352     }
353 
354     if (resource1->sort_index < resource2->sort_index) {
355         return 1;
356     }
357 
358     return 0;
359 }
360 
361 gint
sort_rsc_priority(gconstpointer a,gconstpointer b)362 sort_rsc_priority(gconstpointer a, gconstpointer b)
363 {
364     const pe_resource_t *resource1 = (const pe_resource_t *)a;
365     const pe_resource_t *resource2 = (const pe_resource_t *)b;
366 
367     if (a == NULL && b == NULL) {
368         return 0;
369     }
370     if (a == NULL) {
371         return 1;
372     }
373     if (b == NULL) {
374         return -1;
375     }
376 
377     if (resource1->priority > resource2->priority) {
378         return -1;
379     }
380 
381     if (resource1->priority < resource2->priority) {
382         return 1;
383     }
384 
385     return 0;
386 }
387 
388 static enum pe_quorum_policy
effective_quorum_policy(pe_resource_t * rsc,pe_working_set_t * data_set)389 effective_quorum_policy(pe_resource_t *rsc, pe_working_set_t *data_set)
390 {
391     enum pe_quorum_policy policy = data_set->no_quorum_policy;
392 
393     if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)) {
394         policy = no_quorum_ignore;
395 
396     } else if (data_set->no_quorum_policy == no_quorum_demote) {
397         switch (rsc->role) {
398             case RSC_ROLE_PROMOTED:
399             case RSC_ROLE_UNPROMOTED:
400                 if (rsc->next_role > RSC_ROLE_UNPROMOTED) {
401                     pe__set_next_role(rsc, RSC_ROLE_UNPROMOTED,
402                                       "no-quorum-policy=demote");
403                 }
404                 policy = no_quorum_ignore;
405                 break;
406             default:
407                 policy = no_quorum_stop;
408                 break;
409         }
410     }
411     return policy;
412 }
413 
414 pe_action_t *
custom_action(pe_resource_t * rsc,char * key,const char * task,pe_node_t * on_node,gboolean optional,gboolean save_action,pe_working_set_t * data_set)415 custom_action(pe_resource_t * rsc, char *key, const char *task,
416               pe_node_t * on_node, gboolean optional, gboolean save_action,
417               pe_working_set_t * data_set)
418 {
419     pe_action_t *action = NULL;
420     GList *possible_matches = NULL;
421 
422     CRM_CHECK(key != NULL, return NULL);
423     CRM_CHECK(task != NULL, free(key); return NULL);
424 
425     if (save_action && rsc != NULL) {
426         possible_matches = find_actions(rsc->actions, key, on_node);
427     } else if(save_action) {
428 #if 0
429         action = g_hash_table_lookup(data_set->singletons, key);
430 #else
431         /* More expensive but takes 'node' into account */
432         possible_matches = find_actions(data_set->actions, key, on_node);
433 #endif
434     }
435 
436     if(data_set->singletons == NULL) {
437         data_set->singletons = pcmk__strkey_table(NULL, NULL);
438     }
439 
440     if (possible_matches != NULL) {
441         if (pcmk__list_of_multiple(possible_matches)) {
442             pe_warn("Action %s for %s on %s exists %d times",
443                     task, rsc ? rsc->id : "<NULL>",
444                     on_node ? on_node->details->uname : "<NULL>", g_list_length(possible_matches));
445         }
446 
447         action = g_list_nth_data(possible_matches, 0);
448         pe_rsc_trace(rsc, "Found action %d: %s for %s (%s) on %s",
449                      action->id, task, (rsc? rsc->id : "no resource"),
450                      action->uuid,
451                      (on_node? on_node->details->uname : "no node"));
452         g_list_free(possible_matches);
453     }
454 
455     if (action == NULL) {
456         if (save_action) {
457             pe_rsc_trace(rsc, "Creating action %d (%s): %s for %s (%s) on %s",
458                          data_set->action_id,
459                          (optional? "optional" : "required"),
460                          task, (rsc? rsc->id : "no resource"), key,
461                          (on_node? on_node->details->uname : "no node"));
462         }
463 
464         action = calloc(1, sizeof(pe_action_t));
465         if (save_action) {
466             action->id = data_set->action_id++;
467         } else {
468             action->id = 0;
469         }
470         action->rsc = rsc;
471         action->task = strdup(task);
472         if (on_node) {
473             action->node = pe__copy_node(on_node);
474         }
475         action->uuid = strdup(key);
476 
477         if (pcmk__str_eq(task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
478             // Resource history deletion for a node can be done on the DC
479             pe__set_action_flags(action, pe_action_dc);
480         }
481 
482         pe__set_action_flags(action, pe_action_runnable);
483         if (optional) {
484             pe__set_action_flags(action, pe_action_optional);
485         } else {
486             pe__clear_action_flags(action, pe_action_optional);
487         }
488 
489         action->extra = pcmk__strkey_table(free, free);
490         action->meta = pcmk__strkey_table(free, free);
491 
492         if (save_action) {
493             data_set->actions = g_list_prepend(data_set->actions, action);
494             if(rsc == NULL) {
495                 g_hash_table_insert(data_set->singletons, action->uuid, action);
496             }
497         }
498 
499         if (rsc != NULL) {
500             guint interval_ms = 0;
501 
502             action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
503             parse_op_key(key, NULL, NULL, &interval_ms);
504 
505             unpack_operation(action, action->op_entry, rsc->container, data_set,
506                              interval_ms);
507 
508             if (save_action) {
509                 rsc->actions = g_list_prepend(rsc->actions, action);
510             }
511         }
512     }
513 
514     if (!optional && pcmk_is_set(action->flags, pe_action_optional)) {
515         pe__clear_action_flags(action, pe_action_optional);
516     }
517 
518     if (rsc != NULL) {
519         enum action_tasks a_task = text2task(action->task);
520         enum pe_quorum_policy quorum_policy = effective_quorum_policy(rsc, data_set);
521         int warn_level = LOG_TRACE;
522 
523         if (save_action) {
524             warn_level = LOG_WARNING;
525         }
526 
527         if (!pcmk_is_set(action->flags, pe_action_have_node_attrs)
528             && action->node != NULL && action->op_entry != NULL) {
529             pe_rule_eval_data_t rule_data = {
530                 .node_hash = action->node->details->attrs,
531                 .role = RSC_ROLE_UNKNOWN,
532                 .now = data_set->now,
533                 .match_data = NULL,
534                 .rsc_data = NULL,
535                 .op_data = NULL
536             };
537 
538             pe__set_action_flags(action, pe_action_have_node_attrs);
539             pe__unpack_dataset_nvpairs(action->op_entry, XML_TAG_ATTR_SETS,
540                                        &rule_data, action->extra, NULL,
541                                        FALSE, data_set);
542         }
543 
544         // Make the action optional if its resource is unmanaged
545         if (!pcmk_is_set(action->flags, pe_action_pseudo)
546             && (action->node != NULL)
547             && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)
548             && (g_hash_table_lookup(action->meta,
549                                     XML_LRM_ATTR_INTERVAL_MS) == NULL)) {
550                 pe_rsc_debug(rsc, "%s on %s is optional (%s is unmanaged)",
551                              action->uuid, action->node->details->uname,
552                              action->rsc->id);
553                 pe__set_action_flags(action, pe_action_optional);
554                 // We shouldn't clear runnable here because ... something
555         }
556 
557         // Make the action runnable or unrunnable as appropriate
558         if (pcmk_is_set(action->flags, pe_action_pseudo)) {
559             /* leave untouched */
560 
561         } else if (action->node == NULL) {
562             pe_rsc_trace(rsc, "%s is unrunnable (unallocated)",
563                          action->uuid);
564             pe__clear_action_flags(action, pe_action_runnable);
565 
566         } else if (!pcmk_is_set(action->flags, pe_action_dc)
567                    && !(action->node->details->online)
568                    && (!pe__is_guest_node(action->node)
569                        || action->node->details->remote_requires_reset)) {
570             pe__clear_action_flags(action, pe_action_runnable);
571             do_crm_log(warn_level,
572                        "%s on %s is unrunnable (node is offline)",
573                        action->uuid, action->node->details->uname);
574             if (pcmk_is_set(action->rsc->flags, pe_rsc_managed)
575                 && save_action && a_task == stop_rsc
576                 && action->node->details->unclean == FALSE) {
577                 pe_fence_node(data_set, action->node, "resource actions are unrunnable", FALSE);
578             }
579 
580         } else if (!pcmk_is_set(action->flags, pe_action_dc)
581                    && action->node->details->pending) {
582             pe__clear_action_flags(action, pe_action_runnable);
583             do_crm_log(warn_level,
584                        "Action %s on %s is unrunnable (node is pending)",
585                        action->uuid, action->node->details->uname);
586 
587         } else if (action->needs == rsc_req_nothing) {
588             pe_action_set_reason(action, NULL, TRUE);
589             if (pe__is_guest_node(action->node)
590                 && !pe_can_fence(data_set, action->node)) {
591                 /* An action that requires nothing usually does not require any
592                  * fencing in order to be runnable. However, there is an
593                  * exception: an action cannot be completed if it is on a guest
594                  * node whose host is unclean and cannot be fenced.
595                  */
596                 pe_rsc_debug(rsc, "%s on %s is unrunnable "
597                              "(node's host cannot be fenced)",
598                              action->uuid, action->node->details->uname);
599                 pe__clear_action_flags(action, pe_action_runnable);
600             } else {
601                 pe_rsc_trace(rsc, "%s on %s does not require fencing or quorum",
602                              action->uuid, action->node->details->uname);
603                 pe__set_action_flags(action, pe_action_runnable);
604             }
605 #if 0
606             /*
607              * No point checking this
608              * - if we don't have quorum we can't stonith anyway
609              */
610         } else if (action->needs == rsc_req_stonith) {
611             crm_trace("Action %s requires only stonith", action->uuid);
612             action->runnable = TRUE;
613 #endif
614         } else if (quorum_policy == no_quorum_stop) {
615             pe_rsc_debug(rsc, "%s on %s is unrunnable (no quorum)",
616                          action->uuid, action->node->details->uname);
617             pe_action_set_flag_reason(__func__, __LINE__, action, NULL,
618                                       "no quorum", pe_action_runnable, TRUE);
619 
620         } else if (quorum_policy == no_quorum_freeze) {
621             if (rsc->fns->active(rsc, TRUE) == FALSE || rsc->next_role > rsc->role) {
622                 pe_rsc_debug(rsc, "%s on %s is unrunnable (no quorum)",
623                              action->uuid, action->node->details->uname);
624                 pe_action_set_flag_reason(__func__, __LINE__, action, NULL,
625                                           "quorum freeze", pe_action_runnable,
626                                           TRUE);
627             }
628 
629         } else {
630             //pe_action_set_reason(action, NULL, TRUE);
631             pe__set_action_flags(action, pe_action_runnable);
632         }
633 
634         if (save_action) {
635             switch (a_task) {
636                 case stop_rsc:
637                     pe__set_resource_flags(rsc, pe_rsc_stopping);
638                     break;
639                 case start_rsc:
640                     pe__clear_resource_flags(rsc, pe_rsc_starting);
641                     if (pcmk_is_set(action->flags, pe_action_runnable)) {
642                         pe__set_resource_flags(rsc, pe_rsc_starting);
643                     }
644                     break;
645                 default:
646                     break;
647             }
648         }
649     }
650 
651     free(key);
652     return action;
653 }
654 
655 static bool
valid_stop_on_fail(const char * value)656 valid_stop_on_fail(const char *value)
657 {
658     return !pcmk__strcase_any_of(value, "standby", "demote", "stop", NULL);
659 }
660 
661 static const char *
unpack_operation_on_fail(pe_action_t * action)662 unpack_operation_on_fail(pe_action_t * action)
663 {
664 
665     const char *name = NULL;
666     const char *role = NULL;
667     const char *on_fail = NULL;
668     const char *interval_spec = NULL;
669     const char *enabled = NULL;
670     const char *value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
671 
672     if (pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)
673         && !valid_stop_on_fail(value)) {
674 
675         pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s stop "
676                          "action to default value because '%s' is not "
677                          "allowed for stop", action->rsc->id, value);
678         return NULL;
679 
680     } else if (pcmk__str_eq(action->task, CRMD_ACTION_DEMOTE, pcmk__str_casei) && !value) {
681         // demote on_fail defaults to monitor value for promoted role if present
682         xmlNode *operation = NULL;
683 
684         CRM_CHECK(action->rsc != NULL, return NULL);
685 
686         for (operation = pcmk__xe_first_child(action->rsc->ops_xml);
687              (operation != NULL) && (value == NULL);
688              operation = pcmk__xe_next(operation)) {
689 
690             if (!pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
691                 continue;
692             }
693             name = crm_element_value(operation, "name");
694             role = crm_element_value(operation, "role");
695             on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
696             enabled = crm_element_value(operation, "enabled");
697             interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
698             if (!on_fail) {
699                 continue;
700             } else if (enabled && !crm_is_true(enabled)) {
701                 continue;
702             } else if (!pcmk__str_eq(name, "monitor", pcmk__str_casei)
703                        || !pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
704                                                 RSC_ROLE_PROMOTED_LEGACY_S,
705                                                 NULL)) {
706                 continue;
707             } else if (crm_parse_interval_spec(interval_spec) == 0) {
708                 continue;
709             } else if (pcmk__str_eq(on_fail, "demote", pcmk__str_casei)) {
710                 continue;
711             }
712 
713             value = on_fail;
714         }
715     } else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
716         value = "ignore";
717 
718     } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
719         name = crm_element_value(action->op_entry, "name");
720         role = crm_element_value(action->op_entry, "role");
721         interval_spec = crm_element_value(action->op_entry,
722                                           XML_LRM_ATTR_INTERVAL);
723 
724         if (!pcmk__str_eq(name, CRMD_ACTION_PROMOTE, pcmk__str_casei)
725             && (!pcmk__str_eq(name, CRMD_ACTION_STATUS, pcmk__str_casei)
726                 || !pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
727                                          RSC_ROLE_PROMOTED_LEGACY_S, NULL)
728                 || (crm_parse_interval_spec(interval_spec) == 0))) {
729             pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s %s "
730                              "action to default value because 'demote' is not "
731                              "allowed for it", action->rsc->id, name);
732             return NULL;
733         }
734     }
735 
736     return value;
737 }
738 
739 static xmlNode *
find_min_interval_mon(pe_resource_t * rsc,gboolean include_disabled)740 find_min_interval_mon(pe_resource_t * rsc, gboolean include_disabled)
741 {
742     guint interval_ms = 0;
743     guint min_interval_ms = G_MAXUINT;
744     const char *name = NULL;
745     const char *value = NULL;
746     const char *interval_spec = NULL;
747     xmlNode *op = NULL;
748     xmlNode *operation = NULL;
749 
750     for (operation = pcmk__xe_first_child(rsc->ops_xml);
751          operation != NULL;
752          operation = pcmk__xe_next(operation)) {
753 
754         if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
755             name = crm_element_value(operation, "name");
756             interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
757             value = crm_element_value(operation, "enabled");
758             if (!include_disabled && value && crm_is_true(value) == FALSE) {
759                 continue;
760             }
761 
762             if (!pcmk__str_eq(name, RSC_STATUS, pcmk__str_casei)) {
763                 continue;
764             }
765 
766             interval_ms = crm_parse_interval_spec(interval_spec);
767 
768             if (interval_ms && (interval_ms < min_interval_ms)) {
769                 min_interval_ms = interval_ms;
770                 op = operation;
771             }
772         }
773     }
774 
775     return op;
776 }
777 
778 static int
unpack_start_delay(const char * value,GHashTable * meta)779 unpack_start_delay(const char *value, GHashTable *meta)
780 {
781     int start_delay = 0;
782 
783     if (value != NULL) {
784         start_delay = crm_get_msec(value);
785 
786         if (start_delay < 0) {
787             start_delay = 0;
788         }
789 
790         if (meta) {
791             g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
792                                  pcmk__itoa(start_delay));
793         }
794     }
795 
796     return start_delay;
797 }
798 
799 // true if value contains valid, non-NULL interval origin for recurring op
800 static bool
unpack_interval_origin(const char * value,xmlNode * xml_obj,guint interval_ms,crm_time_t * now,long long * start_delay)801 unpack_interval_origin(const char *value, xmlNode *xml_obj, guint interval_ms,
802                        crm_time_t *now, long long *start_delay)
803 {
804     long long result = 0;
805     guint interval_sec = interval_ms / 1000;
806     crm_time_t *origin = NULL;
807 
808     // Ignore unspecified values and non-recurring operations
809     if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
810         return false;
811     }
812 
813     // Parse interval origin from text
814     origin = crm_time_new(value);
815     if (origin == NULL) {
816         pcmk__config_err("Ignoring '" XML_OP_ATTR_ORIGIN "' for operation "
817                          "'%s' because '%s' is not valid",
818                          (ID(xml_obj)? ID(xml_obj) : "(missing ID)"), value);
819         return false;
820     }
821 
822     // Get seconds since origin (negative if origin is in the future)
823     result = crm_time_get_seconds(now) - crm_time_get_seconds(origin);
824     crm_time_free(origin);
825 
826     // Calculate seconds from closest interval to now
827     result = result % interval_sec;
828 
829     // Calculate seconds remaining until next interval
830     result = ((result <= 0)? 0 : interval_sec) - result;
831     crm_info("Calculated a start delay of %llds for operation '%s'",
832              result,
833              (ID(xml_obj)? ID(xml_obj) : "(unspecified)"));
834 
835     if (start_delay != NULL) {
836         *start_delay = result * 1000; // milliseconds
837     }
838     return true;
839 }
840 
841 static int
unpack_timeout(const char * value)842 unpack_timeout(const char *value)
843 {
844     int timeout_ms = crm_get_msec(value);
845 
846     if (timeout_ms < 0) {
847         timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
848     }
849     return timeout_ms;
850 }
851 
852 int
pe_get_configured_timeout(pe_resource_t * rsc,const char * action,pe_working_set_t * data_set)853 pe_get_configured_timeout(pe_resource_t *rsc, const char *action, pe_working_set_t *data_set)
854 {
855     xmlNode *child = NULL;
856     GHashTable *action_meta = NULL;
857     const char *timeout_spec = NULL;
858     int timeout_ms = 0;
859 
860     pe_rule_eval_data_t rule_data = {
861         .node_hash = NULL,
862         .role = RSC_ROLE_UNKNOWN,
863         .now = data_set->now,
864         .match_data = NULL,
865         .rsc_data = NULL,
866         .op_data = NULL
867     };
868 
869     for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
870          child != NULL; child = crm_next_same_xml(child)) {
871         if (pcmk__str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME),
872                 pcmk__str_casei)) {
873             timeout_spec = crm_element_value(child, XML_ATTR_TIMEOUT);
874             break;
875         }
876     }
877 
878     if (timeout_spec == NULL && data_set->op_defaults) {
879         action_meta = pcmk__strkey_table(free, free);
880         pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS,
881                                    &rule_data, action_meta, NULL, FALSE, data_set);
882         timeout_spec = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
883     }
884 
885     // @TODO check meta-attributes (including versioned meta-attributes)
886     // @TODO maybe use min-interval monitor timeout as default for monitors
887 
888     timeout_ms = crm_get_msec(timeout_spec);
889     if (timeout_ms < 0) {
890         timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
891     }
892 
893     if (action_meta != NULL) {
894         g_hash_table_destroy(action_meta);
895     }
896     return timeout_ms;
897 }
898 
899 #if ENABLE_VERSIONED_ATTRS
900 static void
unpack_versioned_meta(xmlNode * versioned_meta,xmlNode * xml_obj,guint interval_ms,crm_time_t * now)901 unpack_versioned_meta(xmlNode *versioned_meta, xmlNode *xml_obj,
902                       guint interval_ms, crm_time_t *now)
903 {
904     xmlNode *attrs = NULL;
905     xmlNode *attr = NULL;
906 
907     for (attrs = pcmk__xe_first_child(versioned_meta); attrs != NULL;
908          attrs = pcmk__xe_next(attrs)) {
909 
910         for (attr = pcmk__xe_first_child(attrs); attr != NULL;
911              attr = pcmk__xe_next(attr)) {
912 
913             const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
914             const char *value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
915 
916             if (pcmk__str_eq(name, XML_OP_ATTR_START_DELAY, pcmk__str_casei)) {
917                 int start_delay = unpack_start_delay(value, NULL);
918 
919                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
920             } else if (pcmk__str_eq(name, XML_OP_ATTR_ORIGIN, pcmk__str_casei)) {
921                 long long start_delay = 0;
922 
923                 if (unpack_interval_origin(value, xml_obj, interval_ms, now,
924                                            &start_delay)) {
925                     crm_xml_add(attr, XML_NVPAIR_ATTR_NAME,
926                                 XML_OP_ATTR_START_DELAY);
927                     crm_xml_add_ll(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
928                 }
929             } else if (pcmk__str_eq(name, XML_ATTR_TIMEOUT, pcmk__str_casei)) {
930                 int timeout_ms = unpack_timeout(value);
931 
932                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, timeout_ms);
933             }
934         }
935     }
936 }
937 #endif
938 
939 /*!
940  * \brief Unpack operation XML into an action structure
941  *
942  * Unpack an operation's meta-attributes (normalizing the interval, timeout,
943  * and start delay values as integer milliseconds), requirements, and
944  * failure policy.
945  *
946  * \param[in,out] action      Action to unpack into
947  * \param[in]     xml_obj     Operation XML (or NULL if all defaults)
948  * \param[in]     container   Resource that contains affected resource, if any
949  * \param[in]     data_set    Cluster state
950  * \param[in]     interval_ms How frequently to perform the operation
951  */
952 static void
unpack_operation(pe_action_t * action,xmlNode * xml_obj,pe_resource_t * container,pe_working_set_t * data_set,guint interval_ms)953 unpack_operation(pe_action_t * action, xmlNode * xml_obj, pe_resource_t * container,
954                  pe_working_set_t * data_set, guint interval_ms)
955 {
956     int timeout_ms = 0;
957     const char *value = NULL;
958     bool is_probe = pcmk__str_eq(action->task, RSC_STATUS, pcmk__str_casei)
959                     && (interval_ms == 0);
960 #if ENABLE_VERSIONED_ATTRS
961     pe_rsc_action_details_t *rsc_details = NULL;
962 #endif
963 
964     pe_rsc_eval_data_t rsc_rule_data = {
965         .standard = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_CLASS),
966         .provider = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_PROVIDER),
967         .agent = crm_element_value(action->rsc->xml, XML_EXPR_ATTR_TYPE)
968     };
969 
970     pe_op_eval_data_t op_rule_data = {
971         .op_name = action->task,
972         .interval = interval_ms
973     };
974 
975     pe_rule_eval_data_t rule_data = {
976         .node_hash = NULL,
977         .role = RSC_ROLE_UNKNOWN,
978         .now = data_set->now,
979         .match_data = NULL,
980         .rsc_data = &rsc_rule_data,
981         .op_data = &op_rule_data
982     };
983 
984     CRM_CHECK(action && action->rsc, return);
985 
986     // Cluster-wide <op_defaults> <meta_attributes>
987     pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS, &rule_data,
988                                action->meta, NULL, FALSE, data_set);
989 
990     // Determine probe default timeout differently
991     if (is_probe) {
992         xmlNode *min_interval_mon = find_min_interval_mon(action->rsc, FALSE);
993 
994         if (min_interval_mon) {
995             value = crm_element_value(min_interval_mon, XML_ATTR_TIMEOUT);
996             if (value) {
997                 crm_trace("\t%s: Setting default timeout to minimum-interval "
998                           "monitor's timeout '%s'", action->uuid, value);
999                 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1000                                      strdup(value));
1001             }
1002         }
1003     }
1004 
1005     if (xml_obj) {
1006         xmlAttrPtr xIter = NULL;
1007 
1008         // <op> <meta_attributes> take precedence over defaults
1009         pe__unpack_dataset_nvpairs(xml_obj, XML_TAG_META_SETS, &rule_data,
1010                                    action->meta, NULL, TRUE, data_set);
1011 
1012 #if ENABLE_VERSIONED_ATTRS
1013         rsc_details = pe_rsc_action_details(action);
1014 
1015         pe_eval_versioned_attributes(data_set->input, xml_obj,
1016                                      XML_TAG_ATTR_SETS, &rule_data,
1017                                      rsc_details->versioned_parameters,
1018                                      NULL);
1019         pe_eval_versioned_attributes(data_set->input, xml_obj,
1020                                      XML_TAG_META_SETS, &rule_data,
1021                                      rsc_details->versioned_meta,
1022                                      NULL);
1023 #endif
1024 
1025         /* Anything set as an <op> XML property has highest precedence.
1026          * This ensures we use the name and interval from the <op> tag.
1027          */
1028         for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
1029             const char *prop_name = (const char *)xIter->name;
1030             const char *prop_value = crm_element_value(xml_obj, prop_name);
1031 
1032             g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value));
1033         }
1034     }
1035 
1036     g_hash_table_remove(action->meta, "id");
1037 
1038     // Normalize interval to milliseconds
1039     if (interval_ms > 0) {
1040         g_hash_table_replace(action->meta, strdup(XML_LRM_ATTR_INTERVAL),
1041                              crm_strdup_printf("%u", interval_ms));
1042     } else {
1043         g_hash_table_remove(action->meta, XML_LRM_ATTR_INTERVAL);
1044     }
1045 
1046     /*
1047      * Timeout order of precedence:
1048      *   1. pcmk_monitor_timeout (if rsc has pcmk_ra_cap_fence_params
1049      *      and task is start or a probe; pcmk_monitor_timeout works
1050      *      by default for a recurring monitor)
1051      *   2. explicit op timeout on the primitive
1052      *   3. default op timeout
1053      *      a. if probe, then min-interval monitor's timeout
1054      *      b. else, in XML_CIB_TAG_OPCONFIG
1055      *   4. CRM_DEFAULT_OP_TIMEOUT_S
1056      *
1057      * #1 overrides general rule of <op> XML property having highest
1058      * precedence.
1059      */
1060     if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
1061                     pcmk_ra_cap_fence_params)
1062         && (pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)
1063             || is_probe)) {
1064 
1065         GHashTable *params = pe_rsc_params(action->rsc, action->node, data_set);
1066 
1067         value = g_hash_table_lookup(params, "pcmk_monitor_timeout");
1068 
1069         if (value) {
1070             crm_trace("\t%s: Setting timeout to pcmk_monitor_timeout '%s', "
1071                       "overriding default", action->uuid, value);
1072             g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1073                                  strdup(value));
1074         }
1075     }
1076 
1077     // Normalize timeout to positive milliseconds
1078     value = g_hash_table_lookup(action->meta, XML_ATTR_TIMEOUT);
1079     timeout_ms = unpack_timeout(value);
1080     g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1081                          pcmk__itoa(timeout_ms));
1082 
1083     if (!pcmk__strcase_any_of(action->task, RSC_START, RSC_PROMOTE, NULL)) {
1084         action->needs = rsc_req_nothing;
1085         value = "nothing (not start or promote)";
1086 
1087     } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_fencing)) {
1088         action->needs = rsc_req_stonith;
1089         value = "fencing";
1090 
1091     } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_quorum)) {
1092         action->needs = rsc_req_quorum;
1093         value = "quorum";
1094 
1095     } else {
1096         action->needs = rsc_req_nothing;
1097         value = "nothing";
1098     }
1099     pe_rsc_trace(action->rsc, "%s requires %s", action->uuid, value);
1100 
1101     value = unpack_operation_on_fail(action);
1102 
1103     if (value == NULL) {
1104 
1105     } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) {
1106         action->on_fail = action_fail_block;
1107         g_hash_table_insert(action->meta, strdup(XML_OP_ATTR_ON_FAIL), strdup("block"));
1108         value = "block"; // The above could destroy the original string
1109 
1110     } else if (pcmk__str_eq(value, "fence", pcmk__str_casei)) {
1111         action->on_fail = action_fail_fence;
1112         value = "node fencing";
1113 
1114         if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1115             pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for "
1116                              "operation '%s' to 'stop' because 'fence' is not "
1117                              "valid when fencing is disabled", action->uuid);
1118             action->on_fail = action_fail_stop;
1119             action->fail_role = RSC_ROLE_STOPPED;
1120             value = "stop resource";
1121         }
1122 
1123     } else if (pcmk__str_eq(value, "standby", pcmk__str_casei)) {
1124         action->on_fail = action_fail_standby;
1125         value = "node standby";
1126 
1127     } else if (pcmk__strcase_any_of(value, "ignore", "nothing", NULL)) {
1128         action->on_fail = action_fail_ignore;
1129         value = "ignore";
1130 
1131     } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
1132         action->on_fail = action_fail_migrate;
1133         value = "force migration";
1134 
1135     } else if (pcmk__str_eq(value, "stop", pcmk__str_casei)) {
1136         action->on_fail = action_fail_stop;
1137         action->fail_role = RSC_ROLE_STOPPED;
1138         value = "stop resource";
1139 
1140     } else if (pcmk__str_eq(value, "restart", pcmk__str_casei)) {
1141         action->on_fail = action_fail_recover;
1142         value = "restart (and possibly migrate)";
1143 
1144     } else if (pcmk__str_eq(value, "restart-container", pcmk__str_casei)) {
1145         if (container) {
1146             action->on_fail = action_fail_restart_container;
1147             value = "restart container (and possibly migrate)";
1148 
1149         } else {
1150             value = NULL;
1151         }
1152 
1153     } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
1154         action->on_fail = action_fail_demote;
1155         value = "demote instance";
1156 
1157     } else {
1158         pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
1159         value = NULL;
1160     }
1161 
1162     /* defaults */
1163     if (value == NULL && container) {
1164         action->on_fail = action_fail_restart_container;
1165         value = "restart container (and possibly migrate) (default)";
1166 
1167     /* For remote nodes, ensure that any failure that results in dropping an
1168      * active connection to the node results in fencing of the node.
1169      *
1170      * There are only two action failures that don't result in fencing.
1171      * 1. probes - probe failures are expected.
1172      * 2. start - a start failure indicates that an active connection does not already
1173      * exist. The user can set op on-fail=fence if they really want to fence start
1174      * failures. */
1175     } else if (((value == NULL) || !pcmk_is_set(action->rsc->flags, pe_rsc_managed))
1176                && pe__resource_is_remote_conn(action->rsc, data_set)
1177                && !(pcmk__str_eq(action->task, CRMD_ACTION_STATUS, pcmk__str_casei)
1178                     && (interval_ms == 0))
1179                && !pcmk__str_eq(action->task, CRMD_ACTION_START, pcmk__str_casei)) {
1180 
1181         if (!pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
1182             action->on_fail = action_fail_stop;
1183             action->fail_role = RSC_ROLE_STOPPED;
1184             value = "stop unmanaged remote node (enforcing default)";
1185 
1186         } else {
1187             if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1188                 value = "fence remote node (default)";
1189             } else {
1190                 value = "recover remote node connection (default)";
1191             }
1192 
1193             if (action->rsc->remote_reconnect_ms) {
1194                 action->fail_role = RSC_ROLE_STOPPED;
1195             }
1196             action->on_fail = action_fail_reset_remote;
1197         }
1198 
1199     } else if (value == NULL && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
1200         if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1201             action->on_fail = action_fail_fence;
1202             value = "resource fence (default)";
1203 
1204         } else {
1205             action->on_fail = action_fail_block;
1206             value = "resource block (default)";
1207         }
1208 
1209     } else if (value == NULL) {
1210         action->on_fail = action_fail_recover;
1211         value = "restart (and possibly migrate) (default)";
1212     }
1213 
1214     pe_rsc_trace(action->rsc, "%s failure handling: %s",
1215                  action->uuid, value);
1216 
1217     value = NULL;
1218     if (xml_obj != NULL) {
1219         value = g_hash_table_lookup(action->meta, "role_after_failure");
1220         if (value) {
1221             pe_warn_once(pe_wo_role_after,
1222                         "Support for role_after_failure is deprecated and will be removed in a future release");
1223         }
1224     }
1225     if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
1226         action->fail_role = text2role(value);
1227     }
1228     /* defaults */
1229     if (action->fail_role == RSC_ROLE_UNKNOWN) {
1230         if (pcmk__str_eq(action->task, CRMD_ACTION_PROMOTE, pcmk__str_casei)) {
1231             action->fail_role = RSC_ROLE_UNPROMOTED;
1232         } else {
1233             action->fail_role = RSC_ROLE_STARTED;
1234         }
1235     }
1236     pe_rsc_trace(action->rsc, "%s failure results in: %s",
1237                  action->uuid, role2text(action->fail_role));
1238 
1239     value = g_hash_table_lookup(action->meta, XML_OP_ATTR_START_DELAY);
1240     if (value) {
1241         unpack_start_delay(value, action->meta);
1242     } else {
1243         long long start_delay = 0;
1244 
1245         value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
1246         if (unpack_interval_origin(value, xml_obj, interval_ms, data_set->now,
1247                                    &start_delay)) {
1248             g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY),
1249                                  crm_strdup_printf("%lld", start_delay));
1250         }
1251     }
1252 
1253 #if ENABLE_VERSIONED_ATTRS
1254     unpack_versioned_meta(rsc_details->versioned_meta, xml_obj, interval_ms,
1255                           data_set->now);
1256 #endif
1257 }
1258 
1259 static xmlNode *
find_rsc_op_entry_helper(pe_resource_t * rsc,const char * key,gboolean include_disabled)1260 find_rsc_op_entry_helper(pe_resource_t * rsc, const char *key, gboolean include_disabled)
1261 {
1262     guint interval_ms = 0;
1263     gboolean do_retry = TRUE;
1264     char *local_key = NULL;
1265     const char *name = NULL;
1266     const char *value = NULL;
1267     const char *interval_spec = NULL;
1268     char *match_key = NULL;
1269     xmlNode *op = NULL;
1270     xmlNode *operation = NULL;
1271 
1272   retry:
1273     for (operation = pcmk__xe_first_child(rsc->ops_xml); operation != NULL;
1274          operation = pcmk__xe_next(operation)) {
1275 
1276         if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
1277             name = crm_element_value(operation, "name");
1278             interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
1279             value = crm_element_value(operation, "enabled");
1280             if (!include_disabled && value && crm_is_true(value) == FALSE) {
1281                 continue;
1282             }
1283 
1284             interval_ms = crm_parse_interval_spec(interval_spec);
1285             match_key = pcmk__op_key(rsc->id, name, interval_ms);
1286             if (pcmk__str_eq(key, match_key, pcmk__str_casei)) {
1287                 op = operation;
1288             }
1289             free(match_key);
1290 
1291             if (rsc->clone_name) {
1292                 match_key = pcmk__op_key(rsc->clone_name, name, interval_ms);
1293                 if (pcmk__str_eq(key, match_key, pcmk__str_casei)) {
1294                     op = operation;
1295                 }
1296                 free(match_key);
1297             }
1298 
1299             if (op != NULL) {
1300                 free(local_key);
1301                 return op;
1302             }
1303         }
1304     }
1305 
1306     free(local_key);
1307     if (do_retry == FALSE) {
1308         return NULL;
1309     }
1310 
1311     do_retry = FALSE;
1312     if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
1313         local_key = pcmk__op_key(rsc->id, "migrate", 0);
1314         key = local_key;
1315         goto retry;
1316 
1317     } else if (strstr(key, "_notify_")) {
1318         local_key = pcmk__op_key(rsc->id, "notify", 0);
1319         key = local_key;
1320         goto retry;
1321     }
1322 
1323     return NULL;
1324 }
1325 
1326 xmlNode *
find_rsc_op_entry(pe_resource_t * rsc,const char * key)1327 find_rsc_op_entry(pe_resource_t * rsc, const char *key)
1328 {
1329     return find_rsc_op_entry_helper(rsc, key, FALSE);
1330 }
1331 
1332 /*
1333  * Used by the HashTable for-loop
1334  */
1335 void
print_str_str(gpointer key,gpointer value,gpointer user_data)1336 print_str_str(gpointer key, gpointer value, gpointer user_data)
1337 {
1338     crm_trace("%s%s %s ==> %s",
1339               user_data == NULL ? "" : (char *)user_data,
1340               user_data == NULL ? "" : ": ", (char *)key, (char *)value);
1341 }
1342 
1343 void
pe_free_action(pe_action_t * action)1344 pe_free_action(pe_action_t * action)
1345 {
1346     if (action == NULL) {
1347         return;
1348     }
1349     g_list_free_full(action->actions_before, free);     /* pe_action_wrapper_t* */
1350     g_list_free_full(action->actions_after, free);      /* pe_action_wrapper_t* */
1351     if (action->extra) {
1352         g_hash_table_destroy(action->extra);
1353     }
1354     if (action->meta) {
1355         g_hash_table_destroy(action->meta);
1356     }
1357 #if ENABLE_VERSIONED_ATTRS
1358     if (action->rsc) {
1359         pe_free_rsc_action_details(action);
1360     }
1361 #endif
1362     free(action->cancel_task);
1363     free(action->reason);
1364     free(action->task);
1365     free(action->uuid);
1366     free(action->node);
1367     free(action);
1368 }
1369 
1370 GList *
find_recurring_actions(GList * input,pe_node_t * not_on_node)1371 find_recurring_actions(GList *input, pe_node_t * not_on_node)
1372 {
1373     const char *value = NULL;
1374     GList *result = NULL;
1375     GList *gIter = input;
1376 
1377     CRM_CHECK(input != NULL, return NULL);
1378 
1379     for (; gIter != NULL; gIter = gIter->next) {
1380         pe_action_t *action = (pe_action_t *) gIter->data;
1381 
1382         value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL_MS);
1383         if (value == NULL) {
1384             /* skip */
1385         } else if (pcmk__str_eq(value, "0", pcmk__str_casei)) {
1386             /* skip */
1387         } else if (pcmk__str_eq(CRMD_ACTION_CANCEL, action->task, pcmk__str_casei)) {
1388             /* skip */
1389         } else if (not_on_node == NULL) {
1390             crm_trace("(null) Found: %s", action->uuid);
1391             result = g_list_prepend(result, action);
1392 
1393         } else if (action->node == NULL) {
1394             /* skip */
1395         } else if (action->node->details != not_on_node->details) {
1396             crm_trace("Found: %s", action->uuid);
1397             result = g_list_prepend(result, action);
1398         }
1399     }
1400 
1401     return result;
1402 }
1403 
1404 enum action_tasks
get_complex_task(pe_resource_t * rsc,const char * name,gboolean allow_non_atomic)1405 get_complex_task(pe_resource_t * rsc, const char *name, gboolean allow_non_atomic)
1406 {
1407     enum action_tasks task = text2task(name);
1408 
1409     if (rsc == NULL) {
1410         return task;
1411 
1412     } else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
1413         switch (task) {
1414             case stopped_rsc:
1415             case started_rsc:
1416             case action_demoted:
1417             case action_promoted:
1418                 crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
1419                 return task - 1;
1420             default:
1421                 break;
1422         }
1423     }
1424     return task;
1425 }
1426 
1427 pe_action_t *
find_first_action(GList * input,const char * uuid,const char * task,pe_node_t * on_node)1428 find_first_action(GList *input, const char *uuid, const char *task, pe_node_t * on_node)
1429 {
1430     GList *gIter = NULL;
1431 
1432     CRM_CHECK(uuid || task, return NULL);
1433 
1434     for (gIter = input; gIter != NULL; gIter = gIter->next) {
1435         pe_action_t *action = (pe_action_t *) gIter->data;
1436 
1437         if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1438             continue;
1439 
1440         } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1441             continue;
1442 
1443         } else if (on_node == NULL) {
1444             return action;
1445 
1446         } else if (action->node == NULL) {
1447             continue;
1448 
1449         } else if (on_node->details == action->node->details) {
1450             return action;
1451         }
1452     }
1453 
1454     return NULL;
1455 }
1456 
1457 GList *
find_actions(GList * input,const char * key,const pe_node_t * on_node)1458 find_actions(GList *input, const char *key, const pe_node_t *on_node)
1459 {
1460     GList *gIter = input;
1461     GList *result = NULL;
1462 
1463     CRM_CHECK(key != NULL, return NULL);
1464 
1465     for (; gIter != NULL; gIter = gIter->next) {
1466         pe_action_t *action = (pe_action_t *) gIter->data;
1467 
1468         if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1469             crm_trace("%s does not match action %s", key, action->uuid);
1470             continue;
1471 
1472         } else if (on_node == NULL) {
1473             crm_trace("Action %s matches (ignoring node)", key);
1474             result = g_list_prepend(result, action);
1475 
1476         } else if (action->node == NULL) {
1477             crm_trace("Action %s matches (unallocated, assigning to %s)",
1478                       key, on_node->details->uname);
1479 
1480             action->node = pe__copy_node(on_node);
1481             result = g_list_prepend(result, action);
1482 
1483         } else if (on_node->details == action->node->details) {
1484             crm_trace("Action %s on %s matches", key, on_node->details->uname);
1485             result = g_list_prepend(result, action);
1486 
1487         } else {
1488             crm_trace("Action %s on node %s does not match requested node %s",
1489                       key, action->node->details->uname,
1490                       on_node->details->uname);
1491         }
1492     }
1493 
1494     return result;
1495 }
1496 
1497 GList *
find_actions_exact(GList * input,const char * key,const pe_node_t * on_node)1498 find_actions_exact(GList *input, const char *key, const pe_node_t *on_node)
1499 {
1500     GList *result = NULL;
1501 
1502     CRM_CHECK(key != NULL, return NULL);
1503 
1504     if (on_node == NULL) {
1505         crm_trace("Not searching for action %s because node not specified",
1506                   key);
1507         return NULL;
1508     }
1509 
1510     for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1511         pe_action_t *action = (pe_action_t *) gIter->data;
1512 
1513         if (action->node == NULL) {
1514             crm_trace("Skipping comparison of %s vs action %s without node",
1515                       key, action->uuid);
1516 
1517         } else if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1518             crm_trace("Desired action %s doesn't match %s", key, action->uuid);
1519 
1520         } else if (!pcmk__str_eq(on_node->details->id, action->node->details->id, pcmk__str_casei)) {
1521             crm_trace("Action %s desired node ID %s doesn't match %s",
1522                       key, on_node->details->id, action->node->details->id);
1523 
1524         } else {
1525             crm_trace("Action %s matches", key);
1526             result = g_list_prepend(result, action);
1527         }
1528     }
1529 
1530     return result;
1531 }
1532 
1533 /*!
1534  * \brief Find all actions of given type for a resource
1535  *
1536  * \param[in] rsc           Resource to search
1537  * \param[in] node          Find only actions scheduled on this node
1538  * \param[in] task          Action name to search for
1539  * \param[in] require_node  If TRUE, NULL node or action node will not match
1540  *
1541  * \return List of actions found (or NULL if none)
1542  * \note If node is not NULL and require_node is FALSE, matching actions
1543  *       without a node will be assigned to node.
1544  */
1545 GList *
pe__resource_actions(const pe_resource_t * rsc,const pe_node_t * node,const char * task,bool require_node)1546 pe__resource_actions(const pe_resource_t *rsc, const pe_node_t *node,
1547                      const char *task, bool require_node)
1548 {
1549     GList *result = NULL;
1550     char *key = pcmk__op_key(rsc->id, task, 0);
1551 
1552     if (require_node) {
1553         result = find_actions_exact(rsc->actions, key, node);
1554     } else {
1555         result = find_actions(rsc->actions, key, node);
1556     }
1557     free(key);
1558     return result;
1559 }
1560 
1561 static void
resource_node_score(pe_resource_t * rsc,pe_node_t * node,int score,const char * tag)1562 resource_node_score(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag)
1563 {
1564     pe_node_t *match = NULL;
1565 
1566     if ((rsc->exclusive_discover || (node->rsc_discover_mode == pe_discover_never))
1567         && pcmk__str_eq(tag, "symmetric_default", pcmk__str_casei)) {
1568         /* This string comparision may be fragile, but exclusive resources and
1569          * exclusive nodes should not have the symmetric_default constraint
1570          * applied to them.
1571          */
1572         return;
1573 
1574     } else if (rsc->children) {
1575         GList *gIter = rsc->children;
1576 
1577         for (; gIter != NULL; gIter = gIter->next) {
1578             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1579 
1580             resource_node_score(child_rsc, node, score, tag);
1581         }
1582     }
1583 
1584     pe_rsc_trace(rsc, "Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score);
1585     match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
1586     if (match == NULL) {
1587         match = pe__copy_node(node);
1588         g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
1589     }
1590     match->weight = pe__add_scores(match->weight, score);
1591 }
1592 
1593 void
resource_location(pe_resource_t * rsc,pe_node_t * node,int score,const char * tag,pe_working_set_t * data_set)1594 resource_location(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag,
1595                   pe_working_set_t * data_set)
1596 {
1597     if (node != NULL) {
1598         resource_node_score(rsc, node, score, tag);
1599 
1600     } else if (data_set != NULL) {
1601         GList *gIter = data_set->nodes;
1602 
1603         for (; gIter != NULL; gIter = gIter->next) {
1604             pe_node_t *node_iter = (pe_node_t *) gIter->data;
1605 
1606             resource_node_score(rsc, node_iter, score, tag);
1607         }
1608 
1609     } else {
1610         GHashTableIter iter;
1611         pe_node_t *node_iter = NULL;
1612 
1613         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1614         while (g_hash_table_iter_next(&iter, NULL, (void **)&node_iter)) {
1615             resource_node_score(rsc, node_iter, score, tag);
1616         }
1617     }
1618 
1619     if (node == NULL && score == -INFINITY) {
1620         if (rsc->allocated_to) {
1621             crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
1622             free(rsc->allocated_to);
1623             rsc->allocated_to = NULL;
1624         }
1625     }
1626 }
1627 
1628 #define sort_return(an_int, why) do {					\
1629 	free(a_uuid);						\
1630 	free(b_uuid);						\
1631 	crm_trace("%s (%d) %c %s (%d) : %s",				\
1632 		  a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=',	\
1633 		  b_xml_id, b_call_id, why);				\
1634 	return an_int;							\
1635     } while(0)
1636 
1637 gint
sort_op_by_callid(gconstpointer a,gconstpointer b)1638 sort_op_by_callid(gconstpointer a, gconstpointer b)
1639 {
1640     int a_call_id = -1;
1641     int b_call_id = -1;
1642 
1643     char *a_uuid = NULL;
1644     char *b_uuid = NULL;
1645 
1646     const xmlNode *xml_a = a;
1647     const xmlNode *xml_b = b;
1648 
1649     const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID);
1650     const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID);
1651 
1652     if (pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_casei)) {
1653         /* We have duplicate lrm_rsc_op entries in the status
1654          * section which is unlikely to be a good thing
1655          *    - we can handle it easily enough, but we need to get
1656          *    to the bottom of why it's happening.
1657          */
1658         pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
1659         sort_return(0, "duplicate");
1660     }
1661 
1662     crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
1663     crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
1664 
1665     if (a_call_id == -1 && b_call_id == -1) {
1666         /* both are pending ops so it doesn't matter since
1667          *   stops are never pending
1668          */
1669         sort_return(0, "pending");
1670 
1671     } else if (a_call_id >= 0 && a_call_id < b_call_id) {
1672         sort_return(-1, "call id");
1673 
1674     } else if (b_call_id >= 0 && a_call_id > b_call_id) {
1675         sort_return(1, "call id");
1676 
1677     } else if (b_call_id >= 0 && a_call_id == b_call_id) {
1678         /*
1679          * The op and last_failed_op are the same
1680          * Order on last-rc-change
1681          */
1682         time_t last_a = -1;
1683         time_t last_b = -1;
1684 
1685         crm_element_value_epoch(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
1686         crm_element_value_epoch(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
1687 
1688         crm_trace("rc-change: %lld vs %lld",
1689                   (long long) last_a, (long long) last_b);
1690         if (last_a >= 0 && last_a < last_b) {
1691             sort_return(-1, "rc-change");
1692 
1693         } else if (last_b >= 0 && last_a > last_b) {
1694             sort_return(1, "rc-change");
1695         }
1696         sort_return(0, "rc-change");
1697 
1698     } else {
1699         /* One of the inputs is a pending operation
1700          * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
1701          */
1702 
1703         int a_id = -1;
1704         int b_id = -1;
1705 
1706         const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC);
1707         const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC);
1708 
1709         CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1710         if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1711                                      NULL)) {
1712             sort_return(0, "bad magic a");
1713         }
1714         if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1715                                      NULL)) {
1716             sort_return(0, "bad magic b");
1717         }
1718         /* try to determine the relative age of the operation...
1719          * some pending operations (e.g. a start) may have been superseded
1720          *   by a subsequent stop
1721          *
1722          * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1723          */
1724         if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1725             /*
1726              * some of the logic in here may be redundant...
1727              *
1728              * if the UUID from the TE doesn't match then one better
1729              *   be a pending operation.
1730              * pending operations don't survive between elections and joins
1731              *   because we query the LRM directly
1732              */
1733 
1734             if (b_call_id == -1) {
1735                 sort_return(-1, "transition + call");
1736 
1737             } else if (a_call_id == -1) {
1738                 sort_return(1, "transition + call");
1739             }
1740 
1741         } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1742             sort_return(-1, "transition");
1743 
1744         } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1745             sort_return(1, "transition");
1746         }
1747     }
1748 
1749     /* we should never end up here */
1750     CRM_CHECK(FALSE, sort_return(0, "default"));
1751 
1752 }
1753 
1754 time_t
get_effective_time(pe_working_set_t * data_set)1755 get_effective_time(pe_working_set_t * data_set)
1756 {
1757     if(data_set) {
1758         if (data_set->now == NULL) {
1759             crm_trace("Recording a new 'now'");
1760             data_set->now = crm_time_new(NULL);
1761         }
1762         return crm_time_get_seconds_since_epoch(data_set->now);
1763     }
1764 
1765     crm_trace("Defaulting to 'now'");
1766     return time(NULL);
1767 }
1768 
1769 gboolean
get_target_role(pe_resource_t * rsc,enum rsc_role_e * role)1770 get_target_role(pe_resource_t * rsc, enum rsc_role_e * role)
1771 {
1772     enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
1773     const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
1774 
1775     CRM_CHECK(role != NULL, return FALSE);
1776 
1777     if (pcmk__str_eq(value, "started", pcmk__str_null_matches | pcmk__str_casei)
1778         || pcmk__str_eq("default", value, pcmk__str_casei)) {
1779         return FALSE;
1780     }
1781 
1782     local_role = text2role(value);
1783     if (local_role == RSC_ROLE_UNKNOWN) {
1784         pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s "
1785                          "because '%s' is not valid", rsc->id, value);
1786         return FALSE;
1787 
1788     } else if (local_role > RSC_ROLE_STARTED) {
1789         if (pcmk_is_set(uber_parent(rsc)->flags, pe_rsc_promotable)) {
1790             if (local_role > RSC_ROLE_UNPROMOTED) {
1791                 /* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
1792                 return FALSE;
1793             }
1794 
1795         } else {
1796             pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s "
1797                              "because '%s' only makes sense for promotable "
1798                              "clones", rsc->id, value);
1799             return FALSE;
1800         }
1801     }
1802 
1803     *role = local_role;
1804     return TRUE;
1805 }
1806 
1807 gboolean
order_actions(pe_action_t * lh_action,pe_action_t * rh_action,enum pe_ordering order)1808 order_actions(pe_action_t * lh_action, pe_action_t * rh_action, enum pe_ordering order)
1809 {
1810     GList *gIter = NULL;
1811     pe_action_wrapper_t *wrapper = NULL;
1812     GList *list = NULL;
1813 
1814     if (order == pe_order_none) {
1815         return FALSE;
1816     }
1817 
1818     if (lh_action == NULL || rh_action == NULL) {
1819         return FALSE;
1820     }
1821 
1822     crm_trace("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid);
1823 
1824     /* Ensure we never create a dependency on ourselves... it's happened */
1825     CRM_ASSERT(lh_action != rh_action);
1826 
1827     /* Filter dups, otherwise update_action_states() has too much work to do */
1828     gIter = lh_action->actions_after;
1829     for (; gIter != NULL; gIter = gIter->next) {
1830         pe_action_wrapper_t *after = (pe_action_wrapper_t *) gIter->data;
1831 
1832         if (after->action == rh_action && (after->type & order)) {
1833             return FALSE;
1834         }
1835     }
1836 
1837     wrapper = calloc(1, sizeof(pe_action_wrapper_t));
1838     wrapper->action = rh_action;
1839     wrapper->type = order;
1840     list = lh_action->actions_after;
1841     list = g_list_prepend(list, wrapper);
1842     lh_action->actions_after = list;
1843 
1844     wrapper = calloc(1, sizeof(pe_action_wrapper_t));
1845     wrapper->action = lh_action;
1846     wrapper->type = order;
1847     list = rh_action->actions_before;
1848     list = g_list_prepend(list, wrapper);
1849     rh_action->actions_before = list;
1850     return TRUE;
1851 }
1852 
1853 pe_action_t *
get_pseudo_op(const char * name,pe_working_set_t * data_set)1854 get_pseudo_op(const char *name, pe_working_set_t * data_set)
1855 {
1856     pe_action_t *op = NULL;
1857 
1858     if(data_set->singletons) {
1859         op = g_hash_table_lookup(data_set->singletons, name);
1860     }
1861     if (op == NULL) {
1862         op = custom_action(NULL, strdup(name), name, NULL, TRUE, TRUE, data_set);
1863         pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
1864     }
1865 
1866     return op;
1867 }
1868 
1869 void
destroy_ticket(gpointer data)1870 destroy_ticket(gpointer data)
1871 {
1872     pe_ticket_t *ticket = data;
1873 
1874     if (ticket->state) {
1875         g_hash_table_destroy(ticket->state);
1876     }
1877     free(ticket->id);
1878     free(ticket);
1879 }
1880 
1881 pe_ticket_t *
ticket_new(const char * ticket_id,pe_working_set_t * data_set)1882 ticket_new(const char *ticket_id, pe_working_set_t * data_set)
1883 {
1884     pe_ticket_t *ticket = NULL;
1885 
1886     if (pcmk__str_empty(ticket_id)) {
1887         return NULL;
1888     }
1889 
1890     if (data_set->tickets == NULL) {
1891         data_set->tickets = pcmk__strkey_table(free, destroy_ticket);
1892     }
1893 
1894     ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
1895     if (ticket == NULL) {
1896 
1897         ticket = calloc(1, sizeof(pe_ticket_t));
1898         if (ticket == NULL) {
1899             crm_err("Cannot allocate ticket '%s'", ticket_id);
1900             return NULL;
1901         }
1902 
1903         crm_trace("Creaing ticket entry for %s", ticket_id);
1904 
1905         ticket->id = strdup(ticket_id);
1906         ticket->granted = FALSE;
1907         ticket->last_granted = -1;
1908         ticket->standby = FALSE;
1909         ticket->state = pcmk__strkey_table(free, free);
1910 
1911         g_hash_table_insert(data_set->tickets, strdup(ticket->id), ticket);
1912     }
1913 
1914     return ticket;
1915 }
1916 
rsc_printable_id(pe_resource_t * rsc)1917 const char *rsc_printable_id(pe_resource_t *rsc)
1918 {
1919     if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
1920         return ID(rsc->xml);
1921     }
1922     return rsc->id;
1923 }
1924 
1925 void
pe__clear_resource_flags_recursive(pe_resource_t * rsc,uint64_t flags)1926 pe__clear_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
1927 {
1928     pe__clear_resource_flags(rsc, flags);
1929     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1930         pe__clear_resource_flags_recursive((pe_resource_t *) gIter->data, flags);
1931     }
1932 }
1933 
1934 void
pe__clear_resource_flags_on_all(pe_working_set_t * data_set,uint64_t flag)1935 pe__clear_resource_flags_on_all(pe_working_set_t *data_set, uint64_t flag)
1936 {
1937     for (GList *lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
1938         pe_resource_t *r = (pe_resource_t *) lpc->data;
1939         pe__clear_resource_flags_recursive(r, flag);
1940     }
1941 }
1942 
1943 void
pe__set_resource_flags_recursive(pe_resource_t * rsc,uint64_t flags)1944 pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
1945 {
1946     pe__set_resource_flags(rsc, flags);
1947     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1948         pe__set_resource_flags_recursive((pe_resource_t *) gIter->data, flags);
1949     }
1950 }
1951 
1952 static GList *
find_unfencing_devices(GList * candidates,GList * matches)1953 find_unfencing_devices(GList *candidates, GList *matches)
1954 {
1955     for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
1956         pe_resource_t *candidate = gIter->data;
1957         const char *provides = g_hash_table_lookup(candidate->meta,
1958                                                    PCMK_STONITH_PROVIDES);
1959         const char *requires = g_hash_table_lookup(candidate->meta, XML_RSC_ATTR_REQUIRES);
1960 
1961         if(candidate->children) {
1962             matches = find_unfencing_devices(candidate->children, matches);
1963         } else if (!pcmk_is_set(candidate->flags, pe_rsc_fence_device)) {
1964             continue;
1965 
1966         } else if (pcmk__str_eq(provides, "unfencing", pcmk__str_casei) || pcmk__str_eq(requires, "unfencing", pcmk__str_casei)) {
1967             matches = g_list_prepend(matches, candidate);
1968         }
1969     }
1970     return matches;
1971 }
1972 
1973 static int
node_priority_fencing_delay(pe_node_t * node,pe_working_set_t * data_set)1974 node_priority_fencing_delay(pe_node_t * node, pe_working_set_t * data_set)
1975 {
1976     int member_count = 0;
1977     int online_count = 0;
1978     int top_priority = 0;
1979     int lowest_priority = 0;
1980     GList *gIter = NULL;
1981 
1982     // `priority-fencing-delay` is disabled
1983     if (data_set->priority_fencing_delay <= 0) {
1984         return 0;
1985     }
1986 
1987     /* No need to request a delay if the fencing target is not a normal cluster
1988      * member, for example if it's a remote node or a guest node. */
1989     if (node->details->type != node_member) {
1990         return 0;
1991     }
1992 
1993     // No need to request a delay if the fencing target is in our partition
1994     if (node->details->online) {
1995         return 0;
1996     }
1997 
1998     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
1999         pe_node_t *n =  gIter->data;
2000 
2001         if (n->details->type != node_member) {
2002             continue;
2003         }
2004 
2005         member_count ++;
2006 
2007         if (n->details->online) {
2008             online_count++;
2009         }
2010 
2011         if (member_count == 1
2012             || n->details->priority > top_priority) {
2013             top_priority = n->details->priority;
2014         }
2015 
2016         if (member_count == 1
2017             || n->details->priority < lowest_priority) {
2018             lowest_priority = n->details->priority;
2019         }
2020     }
2021 
2022     // No need to delay if we have more than half of the cluster members
2023     if (online_count > member_count / 2) {
2024         return 0;
2025     }
2026 
2027     /* All the nodes have equal priority.
2028      * Any configured corresponding `pcmk_delay_base/max` will be applied. */
2029     if (lowest_priority == top_priority) {
2030         return 0;
2031     }
2032 
2033     if (node->details->priority < top_priority) {
2034         return 0;
2035     }
2036 
2037     return data_set->priority_fencing_delay;
2038 }
2039 
2040 pe_action_t *
pe_fence_op(pe_node_t * node,const char * op,bool optional,const char * reason,bool priority_delay,pe_working_set_t * data_set)2041 pe_fence_op(pe_node_t * node, const char *op, bool optional, const char *reason,
2042             bool priority_delay, pe_working_set_t * data_set)
2043 {
2044     char *op_key = NULL;
2045     pe_action_t *stonith_op = NULL;
2046 
2047     if(op == NULL) {
2048         op = data_set->stonith_action;
2049     }
2050 
2051     op_key = crm_strdup_printf("%s-%s-%s", CRM_OP_FENCE, node->details->uname, op);
2052 
2053     if(data_set->singletons) {
2054         stonith_op = g_hash_table_lookup(data_set->singletons, op_key);
2055     }
2056 
2057     if(stonith_op == NULL) {
2058         stonith_op = custom_action(NULL, op_key, CRM_OP_FENCE, node, TRUE, TRUE, data_set);
2059 
2060         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
2061         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
2062         add_hash_param(stonith_op->meta, "stonith_action", op);
2063 
2064         if (pe__is_guest_or_remote_node(node)
2065             && pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
2066             /* Extra work to detect device changes on remotes
2067              *
2068              * We may do this for all nodes in the future, but for now
2069              * the check_action_definition() based stuff works fine.
2070              */
2071             long max = 1024;
2072             long digests_all_offset = 0;
2073             long digests_secure_offset = 0;
2074 
2075             char *digests_all = calloc(max, sizeof(char));
2076             char *digests_secure = calloc(max, sizeof(char));
2077             GList *matches = find_unfencing_devices(data_set->resources, NULL);
2078 
2079             for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
2080                 pe_resource_t *match = gIter->data;
2081                 const char *agent = g_hash_table_lookup(match->meta,
2082                                                         XML_ATTR_TYPE);
2083                 op_digest_cache_t *data = NULL;
2084 
2085                 data = pe__compare_fencing_digest(match, agent, node, data_set);
2086                 if(data->rc == RSC_DIGEST_ALL) {
2087                     optional = FALSE;
2088                     crm_notice("Unfencing %s (remote): because the definition of %s changed", node->details->uname, match->id);
2089                     if (!pcmk__is_daemon && data_set->priv != NULL) {
2090                         pcmk__output_t *out = data_set->priv;
2091                         out->info(out, "notice: Unfencing %s (remote): because the definition of %s changed",
2092                                   node->details->uname, match->id);
2093                     }
2094                 }
2095 
2096                 digests_all_offset += snprintf(
2097                     digests_all+digests_all_offset, max-digests_all_offset,
2098                     "%s:%s:%s,", match->id, agent, data->digest_all_calc);
2099 
2100                 digests_secure_offset += snprintf(
2101                     digests_secure+digests_secure_offset, max-digests_secure_offset,
2102                     "%s:%s:%s,", match->id, agent, data->digest_secure_calc);
2103             }
2104             g_hash_table_insert(stonith_op->meta,
2105                                 strdup(XML_OP_ATTR_DIGESTS_ALL),
2106                                 digests_all);
2107             g_hash_table_insert(stonith_op->meta,
2108                                 strdup(XML_OP_ATTR_DIGESTS_SECURE),
2109                                 digests_secure);
2110         }
2111 
2112     } else {
2113         free(op_key);
2114     }
2115 
2116     if (data_set->priority_fencing_delay > 0
2117 
2118             /* It's a suitable case where `priority-fencing-delay` applies.
2119              * At least add `priority-fencing-delay` field as an indicator. */
2120         && (priority_delay
2121 
2122             /* Re-calculate priority delay for the suitable case when
2123              * pe_fence_op() is called again by stage6() after node priority has
2124              * been actually calculated with native_add_running() */
2125             || g_hash_table_lookup(stonith_op->meta,
2126                                    XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY) != NULL)) {
2127 
2128             /* Add `priority-fencing-delay` to the fencing op even if it's 0 for
2129              * the targeting node. So that it takes precedence over any possible
2130              * `pcmk_delay_base/max`.
2131              */
2132             char *delay_s = pcmk__itoa(node_priority_fencing_delay(node, data_set));
2133 
2134             g_hash_table_insert(stonith_op->meta,
2135                                 strdup(XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY),
2136                                 delay_s);
2137     }
2138 
2139     if(optional == FALSE && pe_can_fence(data_set, node)) {
2140         pe_action_required(stonith_op, NULL, reason);
2141     } else if(reason && stonith_op->reason == NULL) {
2142         stonith_op->reason = strdup(reason);
2143     }
2144 
2145     return stonith_op;
2146 }
2147 
2148 void
trigger_unfencing(pe_resource_t * rsc,pe_node_t * node,const char * reason,pe_action_t * dependency,pe_working_set_t * data_set)2149 trigger_unfencing(
2150     pe_resource_t * rsc, pe_node_t *node, const char *reason, pe_action_t *dependency, pe_working_set_t * data_set)
2151 {
2152     if (!pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
2153         /* No resources require it */
2154         return;
2155 
2156     } else if ((rsc != NULL)
2157                && !pcmk_is_set(rsc->flags, pe_rsc_fence_device)) {
2158         /* Wasn't a stonith device */
2159         return;
2160 
2161     } else if(node
2162               && node->details->online
2163               && node->details->unclean == FALSE
2164               && node->details->shutdown == FALSE) {
2165         pe_action_t *unfence = pe_fence_op(node, "on", FALSE, reason, FALSE, data_set);
2166 
2167         if(dependency) {
2168             order_actions(unfence, dependency, pe_order_optional);
2169         }
2170 
2171     } else if(rsc) {
2172         GHashTableIter iter;
2173 
2174         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
2175         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
2176             if(node->details->online && node->details->unclean == FALSE && node->details->shutdown == FALSE) {
2177                 trigger_unfencing(rsc, node, reason, dependency, data_set);
2178             }
2179         }
2180     }
2181 }
2182 
2183 gboolean
add_tag_ref(GHashTable * tags,const char * tag_name,const char * obj_ref)2184 add_tag_ref(GHashTable * tags, const char * tag_name,  const char * obj_ref)
2185 {
2186     pe_tag_t *tag = NULL;
2187     GList *gIter = NULL;
2188     gboolean is_existing = FALSE;
2189 
2190     CRM_CHECK(tags && tag_name && obj_ref, return FALSE);
2191 
2192     tag = g_hash_table_lookup(tags, tag_name);
2193     if (tag == NULL) {
2194         tag = calloc(1, sizeof(pe_tag_t));
2195         if (tag == NULL) {
2196             return FALSE;
2197         }
2198         tag->id = strdup(tag_name);
2199         tag->refs = NULL;
2200         g_hash_table_insert(tags, strdup(tag_name), tag);
2201     }
2202 
2203     for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
2204         const char *existing_ref = (const char *) gIter->data;
2205 
2206         if (pcmk__str_eq(existing_ref, obj_ref, pcmk__str_none)){
2207             is_existing = TRUE;
2208             break;
2209         }
2210     }
2211 
2212     if (is_existing == FALSE) {
2213         tag->refs = g_list_append(tag->refs, strdup(obj_ref));
2214         crm_trace("Added: tag=%s ref=%s", tag->id, obj_ref);
2215     }
2216 
2217     return TRUE;
2218 }
2219 
pe_action_set_flag_reason(const char * function,long line,pe_action_t * action,pe_action_t * reason,const char * text,enum pe_action_flags flags,bool overwrite)2220 void pe_action_set_flag_reason(const char *function, long line,
2221                                pe_action_t *action, pe_action_t *reason, const char *text,
2222                                enum pe_action_flags flags, bool overwrite)
2223 {
2224     bool unset = FALSE;
2225     bool update = FALSE;
2226     const char *change = NULL;
2227 
2228     if (pcmk_is_set(flags, pe_action_runnable)) {
2229         unset = TRUE;
2230         change = "unrunnable";
2231     } else if (pcmk_is_set(flags, pe_action_optional)) {
2232         unset = TRUE;
2233         change = "required";
2234     } else if (pcmk_is_set(flags, pe_action_migrate_runnable)) {
2235         unset = TRUE;
2236         overwrite = TRUE;
2237         change = "unrunnable";
2238     } else if (pcmk_is_set(flags, pe_action_dangle)) {
2239         change = "dangling";
2240     } else if (pcmk_is_set(flags, pe_action_requires_any)) {
2241         change = "required";
2242     } else {
2243         crm_err("Unknown flag change to %x by %s: 0x%s",
2244                 flags, action->uuid, (reason? reason->uuid : "0"));
2245     }
2246 
2247     if(unset) {
2248         if (pcmk_is_set(action->flags, flags)) {
2249             pe__clear_action_flags_as(function, line, action, flags);
2250             update = TRUE;
2251         }
2252 
2253     } else {
2254         if (!pcmk_is_set(action->flags, flags)) {
2255             pe__set_action_flags_as(function, line, action, flags);
2256             update = TRUE;
2257         }
2258     }
2259 
2260     if((change && update) || text) {
2261         char *reason_text = NULL;
2262         if(reason == NULL) {
2263             pe_action_set_reason(action, text, overwrite);
2264 
2265         } else if(reason->rsc == NULL) {
2266             reason_text = crm_strdup_printf("%s %s%c %s", change, reason->task, text?':':0, text?text:"");
2267         } else {
2268             reason_text = crm_strdup_printf("%s %s %s%c %s", change, reason->rsc->id, reason->task, text?':':0, text?text:"NA");
2269         }
2270 
2271         if(reason_text && action->rsc != reason->rsc) {
2272             pe_action_set_reason(action, reason_text, overwrite);
2273         }
2274         free(reason_text);
2275     }
2276  }
2277 
pe_action_set_reason(pe_action_t * action,const char * reason,bool overwrite)2278 void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite)
2279 {
2280     if (action->reason != NULL && overwrite) {
2281         pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
2282                      action->uuid, action->reason, crm_str(reason));
2283         free(action->reason);
2284     } else if (action->reason == NULL) {
2285         pe_rsc_trace(action->rsc, "Set %s reason to '%s'",
2286                      action->uuid, crm_str(reason));
2287     } else {
2288         // crm_assert(action->reason != NULL && !overwrite);
2289         return;
2290     }
2291 
2292     if (reason != NULL) {
2293         action->reason = strdup(reason);
2294     } else {
2295         action->reason = NULL;
2296     }
2297 }
2298 
2299 /*!
2300  * \internal
2301  * \brief Check whether shutdown has been requested for a node
2302  *
2303  * \param[in] node  Node to check
2304  *
2305  * \return TRUE if node has shutdown attribute set and nonzero, FALSE otherwise
2306  * \note This differs from simply using node->details->shutdown in that it can
2307  *       be used before that has been determined (and in fact to determine it),
2308  *       and it can also be used to distinguish requested shutdown from implicit
2309  *       shutdown of remote nodes by virtue of their connection stopping.
2310  */
2311 bool
pe__shutdown_requested(pe_node_t * node)2312 pe__shutdown_requested(pe_node_t *node)
2313 {
2314     const char *shutdown = pe_node_attribute_raw(node, XML_CIB_ATTR_SHUTDOWN);
2315 
2316     return !pcmk__str_eq(shutdown, "0", pcmk__str_null_matches);
2317 }
2318 
2319 /*!
2320  * \internal
2321  * \brief Update a data set's "recheck by" time
2322  *
2323  * \param[in]     recheck   Epoch time when recheck should happen
2324  * \param[in,out] data_set  Current working set
2325  */
2326 void
pe__update_recheck_time(time_t recheck,pe_working_set_t * data_set)2327 pe__update_recheck_time(time_t recheck, pe_working_set_t *data_set)
2328 {
2329     if ((recheck > get_effective_time(data_set))
2330         && ((data_set->recheck_by == 0)
2331             || (data_set->recheck_by > recheck))) {
2332         data_set->recheck_by = recheck;
2333     }
2334 }
2335 
2336 /*!
2337  * \internal
2338  * \brief Wrapper for pe_unpack_nvpairs() using a cluster working set
2339  */
2340 void
pe__unpack_dataset_nvpairs(xmlNode * xml_obj,const char * set_name,pe_rule_eval_data_t * rule_data,GHashTable * hash,const char * always_first,gboolean overwrite,pe_working_set_t * data_set)2341 pe__unpack_dataset_nvpairs(xmlNode *xml_obj, const char *set_name,
2342                            pe_rule_eval_data_t *rule_data, GHashTable *hash,
2343                            const char *always_first, gboolean overwrite,
2344                            pe_working_set_t *data_set)
2345 {
2346     crm_time_t *next_change = crm_time_new_undefined();
2347 
2348     pe_eval_nvpairs(data_set->input, xml_obj, set_name, rule_data, hash,
2349                     always_first, overwrite, next_change);
2350     if (crm_time_is_defined(next_change)) {
2351         time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change);
2352 
2353         pe__update_recheck_time(recheck, data_set);
2354     }
2355     crm_time_free(next_change);
2356 }
2357 
2358 bool
pe__resource_is_disabled(pe_resource_t * rsc)2359 pe__resource_is_disabled(pe_resource_t *rsc)
2360 {
2361     const char *target_role = NULL;
2362 
2363     CRM_CHECK(rsc != NULL, return false);
2364     target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
2365     if (target_role) {
2366         enum rsc_role_e target_role_e = text2role(target_role);
2367 
2368         if ((target_role_e == RSC_ROLE_STOPPED)
2369             || ((target_role_e == RSC_ROLE_UNPROMOTED)
2370                 && pcmk_is_set(uber_parent(rsc)->flags, pe_rsc_promotable))) {
2371             return true;
2372         }
2373     }
2374     return false;
2375 }
2376 
2377 /*!
2378  * \internal
2379  * \brief Create an action to clear a resource's history from CIB
2380  *
2381  * \param[in] rsc   Resource to clear
2382  * \param[in] node  Node to clear history on
2383  *
2384  * \return New action to clear resource history
2385  */
2386 pe_action_t *
pe__clear_resource_history(pe_resource_t * rsc,pe_node_t * node,pe_working_set_t * data_set)2387 pe__clear_resource_history(pe_resource_t *rsc, pe_node_t *node,
2388                            pe_working_set_t *data_set)
2389 {
2390     char *key = NULL;
2391 
2392     CRM_ASSERT(rsc && node);
2393     key = pcmk__op_key(rsc->id, CRM_OP_LRM_DELETE, 0);
2394     return custom_action(rsc, key, CRM_OP_LRM_DELETE, node, FALSE, TRUE,
2395                          data_set);
2396 }
2397 
2398 bool
pe__rsc_running_on_any(pe_resource_t * rsc,GList * node_list)2399 pe__rsc_running_on_any(pe_resource_t *rsc, GList *node_list)
2400 {
2401     for (GList *ele = rsc->running_on; ele; ele = ele->next) {
2402         pe_node_t *node = (pe_node_t *) ele->data;
2403         if (pcmk__str_in_list(node_list, node->details->uname, pcmk__str_casei)) {
2404             return true;
2405         }
2406     }
2407 
2408     return false;
2409 }
2410 
2411 bool
pcmk__rsc_filtered_by_node(pe_resource_t * rsc,GList * only_node)2412 pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node)
2413 {
2414     return (rsc->fns->active(rsc, FALSE) && !pe__rsc_running_on_any(rsc, only_node));
2415 }
2416 
2417 GList *
pe__filter_rsc_list(GList * rscs,GList * filter)2418 pe__filter_rsc_list(GList *rscs, GList *filter)
2419 {
2420     GList *retval = NULL;
2421 
2422     for (GList *gIter = rscs; gIter; gIter = gIter->next) {
2423         pe_resource_t *rsc = (pe_resource_t *) gIter->data;
2424 
2425         /* I think the second condition is safe here for all callers of this
2426          * function.  If not, it needs to move into pe__node_text.
2427          */
2428         if (pcmk__str_in_list(filter, rsc_printable_id(rsc), pcmk__str_none) ||
2429             (rsc->parent && pcmk__str_in_list(filter, rsc_printable_id(rsc->parent), pcmk__str_none))) {
2430             retval = g_list_prepend(retval, rsc);
2431         }
2432     }
2433 
2434     return retval;
2435 }
2436 
2437 GList *
pe__build_node_name_list(pe_working_set_t * data_set,const char * s)2438 pe__build_node_name_list(pe_working_set_t *data_set, const char *s) {
2439     GList *nodes = NULL;
2440 
2441     if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
2442         /* Nothing was given so return a list of all node names.  Or, '*' was
2443          * given.  This would normally fall into the pe__unames_with_tag branch
2444          * where it will return an empty list.  Catch it here instead.
2445          */
2446         nodes = g_list_prepend(nodes, strdup("*"));
2447     } else {
2448         pe_node_t *node = pe_find_node(data_set->nodes, s);
2449 
2450         if (node) {
2451             /* The given string was a valid uname for a node.  Return a
2452              * singleton list containing just that uname.
2453              */
2454             nodes = g_list_prepend(nodes, strdup(s));
2455         } else {
2456             /* The given string was not a valid uname.  It's either a tag or
2457              * it's a typo or something.  In the first case, we'll return a
2458              * list of all the unames of the nodes with the given tag.  In the
2459              * second case, we'll return a NULL pointer and nothing will
2460              * get displayed.
2461              */
2462             nodes = pe__unames_with_tag(data_set, s);
2463         }
2464     }
2465 
2466     return nodes;
2467 }
2468 
2469 GList *
pe__build_rsc_list(pe_working_set_t * data_set,const char * s)2470 pe__build_rsc_list(pe_working_set_t *data_set, const char *s) {
2471     GList *resources = NULL;
2472 
2473     if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
2474         resources = g_list_prepend(resources, strdup("*"));
2475     } else {
2476         pe_resource_t *rsc = pe_find_resource_with_flags(data_set->resources, s,
2477                                                          pe_find_renamed|pe_find_any);
2478 
2479         if (rsc) {
2480             /* A colon in the name we were given means we're being asked to filter
2481              * on a specific instance of a cloned resource.  Put that exact string
2482              * into the filter list.  Otherwise, use the printable ID of whatever
2483              * resource was found that matches what was asked for.
2484              */
2485             if (strstr(s, ":") != NULL) {
2486                 resources = g_list_prepend(resources, strdup(rsc->id));
2487             } else {
2488                 resources = g_list_prepend(resources, strdup(rsc_printable_id(rsc)));
2489             }
2490         } else {
2491             /* The given string was not a valid resource name.  It's either
2492              * a tag or it's a typo or something.  See build_uname_list for
2493              * more detail.
2494              */
2495             resources = pe__rscs_with_tag(data_set, s);
2496         }
2497     }
2498 
2499     return resources;
2500 }
2501