1 /*
2  * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This source code is licensed under the GNU Lesser General Public License
5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
6  */
7 
8 #include <crm_internal.h>
9 
10 #include <crm/pengine/rules.h>
11 #include <crm/pengine/status.h>
12 #include <crm/pengine/complex.h>
13 #include <crm/pengine/internal.h>
14 #include <unpack.h>
15 #include <crm/msg_xml.h>
16 #include <pe_status_private.h>
17 
18 #define VARIANT_NATIVE 1
19 #include "./variant.h"
20 
21 /*!
22  * \internal
23  * \brief Check whether a resource is active on multiple nodes
24  */
25 static bool
is_multiply_active(pe_resource_t * rsc)26 is_multiply_active(pe_resource_t *rsc)
27 {
28     unsigned int count = 0;
29 
30     if (rsc->variant == pe_native) {
31         pe__find_active_requires(rsc, &count);
32     }
33     return count > 1;
34 }
35 
36 static void
native_priority_to_node(pe_resource_t * rsc,pe_node_t * node)37 native_priority_to_node(pe_resource_t * rsc, pe_node_t * node)
38 {
39     int priority = 0;
40 
41     if (rsc->priority == 0) {
42         return;
43     }
44 
45     if (rsc->role == RSC_ROLE_MASTER) {
46         // Promoted instance takes base priority + 1
47         priority = rsc->priority + 1;
48 
49     } else {
50         priority = rsc->priority;
51     }
52 
53     node->details->priority += priority;
54     pe_rsc_trace(rsc, "Node '%s' now has priority %d with %s'%s' (priority: %d%s)",
55                  node->details->uname, node->details->priority,
56                  rsc->role == RSC_ROLE_MASTER ? "promoted " : "",
57                  rsc->id, rsc->priority,
58                  rsc->role == RSC_ROLE_MASTER ? " + 1" : "");
59 
60     /* Priority of a resource running on a guest node is added to the cluster
61      * node as well. */
62     if (node->details->remote_rsc
63         && node->details->remote_rsc->container) {
64         GListPtr gIter = node->details->remote_rsc->container->running_on;
65 
66         for (; gIter != NULL; gIter = gIter->next) {
67             pe_node_t *a_node = gIter->data;
68 
69             a_node->details->priority += priority;
70             pe_rsc_trace(rsc, "Node '%s' now has priority %d with %s'%s' (priority: %d%s) "
71                          "from guest node '%s'",
72                          a_node->details->uname, a_node->details->priority,
73                          rsc->role == RSC_ROLE_MASTER ? "promoted " : "",
74                          rsc->id, rsc->priority,
75                          rsc->role == RSC_ROLE_MASTER ? " + 1" : "",
76                          node->details->uname);
77         }
78     }
79 }
80 
81 void
native_add_running(resource_t * rsc,node_t * node,pe_working_set_t * data_set)82 native_add_running(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
83 {
84     GListPtr gIter = rsc->running_on;
85 
86     CRM_CHECK(node != NULL, return);
87     for (; gIter != NULL; gIter = gIter->next) {
88         node_t *a_node = (node_t *) gIter->data;
89 
90         CRM_CHECK(a_node != NULL, return);
91         if (safe_str_eq(a_node->details->id, node->details->id)) {
92             return;
93         }
94     }
95 
96     pe_rsc_trace(rsc, "Adding %s to %s %s", rsc->id, node->details->uname,
97                  is_set(rsc->flags, pe_rsc_managed)?"":"(unmanaged)");
98 
99     rsc->running_on = g_list_append(rsc->running_on, node);
100     if (rsc->variant == pe_native) {
101         node->details->running_rsc = g_list_append(node->details->running_rsc, rsc);
102 
103         native_priority_to_node(rsc, node);
104     }
105 
106     if (rsc->variant == pe_native && node->details->maintenance) {
107         clear_bit(rsc->flags, pe_rsc_managed);
108     }
109 
110     if (is_not_set(rsc->flags, pe_rsc_managed)) {
111         resource_t *p = rsc->parent;
112 
113         pe_rsc_info(rsc, "resource %s isn't managed", rsc->id);
114         resource_location(rsc, node, INFINITY, "not_managed_default", data_set);
115 
116         while(p && node->details->online) {
117             /* add without the additional location constraint */
118             p->running_on = g_list_append(p->running_on, node);
119             p = p->parent;
120         }
121         return;
122     }
123 
124     if (is_multiply_active(rsc)) {
125         switch (rsc->recovery_type) {
126             case recovery_stop_only:
127                 {
128                     GHashTableIter gIter;
129                     node_t *local_node = NULL;
130 
131                     /* make sure it doesn't come up again */
132                     if (rsc->allowed_nodes != NULL) {
133                         g_hash_table_destroy(rsc->allowed_nodes);
134                     }
135                     rsc->allowed_nodes = node_hash_from_list(data_set->nodes);
136                     g_hash_table_iter_init(&gIter, rsc->allowed_nodes);
137                     while (g_hash_table_iter_next(&gIter, NULL, (void **)&local_node)) {
138                         local_node->weight = -INFINITY;
139                     }
140                 }
141                 break;
142             case recovery_stop_start:
143                 break;
144             case recovery_block:
145                 clear_bit(rsc->flags, pe_rsc_managed);
146                 set_bit(rsc->flags, pe_rsc_block);
147 
148                 /* If the resource belongs to a group or bundle configured with
149                  * multiple-active=block, block the entire entity.
150                  */
151                 if (rsc->parent
152                     && (rsc->parent->variant == pe_group || rsc->parent->variant == pe_container)
153                     && rsc->parent->recovery_type == recovery_block) {
154                     GListPtr gIter = rsc->parent->children;
155 
156                     for (; gIter != NULL; gIter = gIter->next) {
157                         resource_t *child = (resource_t *) gIter->data;
158 
159                         clear_bit(child->flags, pe_rsc_managed);
160                         set_bit(child->flags, pe_rsc_block);
161                     }
162                 }
163                 break;
164         }
165         crm_debug("%s is active on multiple nodes including %s: %s",
166                   rsc->id, node->details->uname,
167                   recovery2text(rsc->recovery_type));
168 
169     } else {
170         pe_rsc_trace(rsc, "Resource %s is active on: %s", rsc->id, node->details->uname);
171     }
172 
173     if (rsc->parent != NULL) {
174         native_add_running(rsc->parent, node, data_set);
175     }
176 }
177 
178 static void
recursive_clear_unique(pe_resource_t * rsc)179 recursive_clear_unique(pe_resource_t *rsc)
180 {
181     clear_bit(rsc->flags, pe_rsc_unique);
182     add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE, XML_BOOLEAN_FALSE);
183 
184     for (GList *child = rsc->children; child != NULL; child = child->next) {
185         recursive_clear_unique((pe_resource_t *) child->data);
186     }
187 }
188 
189 gboolean
native_unpack(resource_t * rsc,pe_working_set_t * data_set)190 native_unpack(resource_t * rsc, pe_working_set_t * data_set)
191 {
192     resource_t *parent = uber_parent(rsc);
193     native_variant_data_t *native_data = NULL;
194     const char *standard = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
195     uint32_t ra_caps = pcmk_get_ra_caps(standard);
196 
197     pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
198 
199     native_data = calloc(1, sizeof(native_variant_data_t));
200     rsc->variant_opaque = native_data;
201 
202     // Only some agent standards support unique and promotable clones
203     if (is_not_set(ra_caps, pcmk_ra_cap_unique)
204         && is_set(rsc->flags, pe_rsc_unique) && pe_rsc_is_clone(parent)) {
205 
206         /* @COMPAT We should probably reject this situation as an error (as we
207          * do for promotable below) rather than warn and convert, but that would
208          * be a backward-incompatible change that we should probably do with a
209          * transform at a schema major version bump.
210          */
211         pe__force_anon(standard, parent, rsc->id, data_set);
212 
213         /* Clear globally-unique on the parent and all its descendents unpacked
214          * so far (clearing the parent should make any future children unpacking
215          * correct). We have to clear this resource explicitly because it isn't
216          * hooked into the parent's children yet.
217          */
218         recursive_clear_unique(parent);
219         recursive_clear_unique(rsc);
220     }
221     if (is_not_set(ra_caps, pcmk_ra_cap_promotable)) {
222         const char *stateful = g_hash_table_lookup(parent->meta, "stateful");
223 
224         if (safe_str_eq(stateful, XML_BOOLEAN_TRUE)) {
225             pe_err
226                 ("Resource %s is of type %s and therefore cannot be used as a master/slave resource",
227                  rsc->id, standard);
228             return FALSE;
229         }
230     }
231     return TRUE;
232 }
233 
234 static bool
rsc_is_on_node(resource_t * rsc,node_t * node,int flags)235 rsc_is_on_node(resource_t *rsc, node_t *node, int flags)
236 {
237     pe_rsc_trace(rsc, "Checking whether %s is on %s",
238                  rsc->id, node->details->uname);
239 
240     if (is_set(flags, pe_find_current) && rsc->running_on) {
241 
242         for (GListPtr iter = rsc->running_on; iter; iter = iter->next) {
243             node_t *loc = (node_t *) iter->data;
244 
245             if (loc->details == node->details) {
246                 return TRUE;
247             }
248         }
249 
250     } else if (is_set(flags, pe_find_inactive) && (rsc->running_on == NULL)) {
251         return TRUE;
252 
253     } else if (is_not_set(flags, pe_find_current) && rsc->allocated_to
254                && (rsc->allocated_to->details == node->details)) {
255         return TRUE;
256     }
257     return FALSE;
258 }
259 
260 resource_t *
native_find_rsc(resource_t * rsc,const char * id,node_t * on_node,int flags)261 native_find_rsc(resource_t * rsc, const char *id, node_t * on_node, int flags)
262 {
263     bool match = FALSE;
264     resource_t *result = NULL;
265 
266     CRM_CHECK(id && rsc && rsc->id, return NULL);
267 
268     if (flags & pe_find_clone) {
269         const char *rid = ID(rsc->xml);
270 
271         if (!pe_rsc_is_clone(uber_parent(rsc))) {
272             match = FALSE;
273 
274         } else if (!strcmp(id, rsc->id) || safe_str_eq(id, rid)) {
275             match = TRUE;
276         }
277 
278     } else if (!strcmp(id, rsc->id)) {
279         match = TRUE;
280 
281     } else if (is_set(flags, pe_find_renamed)
282                && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) {
283         match = TRUE;
284 
285     } else if (is_set(flags, pe_find_any)
286                || (is_set(flags, pe_find_anon)
287                    && is_not_set(rsc->flags, pe_rsc_unique))) {
288         match = pe_base_name_eq(rsc, id);
289     }
290 
291     if (match && on_node) {
292         bool match_node = rsc_is_on_node(rsc, on_node, flags);
293 
294         if (match_node == FALSE) {
295             match = FALSE;
296         }
297     }
298 
299     if (match) {
300         return rsc;
301     }
302 
303     for (GListPtr gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
304         resource_t *child = (resource_t *) gIter->data;
305 
306         result = rsc->fns->find_rsc(child, id, on_node, flags);
307         if (result) {
308             return result;
309         }
310     }
311     return NULL;
312 }
313 
314 char *
native_parameter(resource_t * rsc,node_t * node,gboolean create,const char * name,pe_working_set_t * data_set)315 native_parameter(resource_t * rsc, node_t * node, gboolean create, const char *name,
316                  pe_working_set_t * data_set)
317 {
318     char *value_copy = NULL;
319     const char *value = NULL;
320     GHashTable *hash = NULL;
321     GHashTable *local_hash = NULL;
322 
323     CRM_CHECK(rsc != NULL, return NULL);
324     CRM_CHECK(name != NULL && strlen(name) != 0, return NULL);
325 
326     pe_rsc_trace(rsc, "Looking up %s in %s", name, rsc->id);
327 
328     if (create || g_hash_table_size(rsc->parameters) == 0) {
329         if (node != NULL) {
330             pe_rsc_trace(rsc, "Creating hash with node %s", node->details->uname);
331         } else {
332             pe_rsc_trace(rsc, "Creating default hash");
333         }
334 
335         local_hash = crm_str_table_new();
336 
337         get_rsc_attributes(local_hash, rsc, node, data_set);
338 
339         hash = local_hash;
340     } else {
341         hash = rsc->parameters;
342     }
343 
344     value = g_hash_table_lookup(hash, name);
345     if (value == NULL) {
346         /* try meta attributes instead */
347         value = g_hash_table_lookup(rsc->meta, name);
348     }
349 
350     if (value != NULL) {
351         value_copy = strdup(value);
352     }
353     if (local_hash != NULL) {
354         g_hash_table_destroy(local_hash);
355     }
356     return value_copy;
357 }
358 
359 gboolean
native_active(resource_t * rsc,gboolean all)360 native_active(resource_t * rsc, gboolean all)
361 {
362     GListPtr gIter = rsc->running_on;
363 
364     for (; gIter != NULL; gIter = gIter->next) {
365         node_t *a_node = (node_t *) gIter->data;
366 
367         if (a_node->details->unclean) {
368             crm_debug("Resource %s: node %s is unclean", rsc->id, a_node->details->uname);
369             return TRUE;
370         } else if (a_node->details->online == FALSE) {
371             crm_debug("Resource %s: node %s is offline", rsc->id, a_node->details->uname);
372         } else {
373             crm_debug("Resource %s active on %s", rsc->id, a_node->details->uname);
374             return TRUE;
375         }
376     }
377 
378     return FALSE;
379 }
380 
381 struct print_data_s {
382     long options;
383     void *print_data;
384 };
385 
386 static void
native_print_attr(gpointer key,gpointer value,gpointer user_data)387 native_print_attr(gpointer key, gpointer value, gpointer user_data)
388 {
389     long options = ((struct print_data_s *)user_data)->options;
390     void *print_data = ((struct print_data_s *)user_data)->print_data;
391 
392     status_print("Option: %s = %s\n", (char *)key, (char *)value);
393 }
394 
395 static const char *
native_pending_state(resource_t * rsc)396 native_pending_state(resource_t * rsc)
397 {
398     const char *pending_state = NULL;
399 
400     if (safe_str_eq(rsc->pending_task, CRMD_ACTION_START)) {
401         pending_state = "Starting";
402 
403     } else if (safe_str_eq(rsc->pending_task, CRMD_ACTION_STOP)) {
404         pending_state = "Stopping";
405 
406     } else if (safe_str_eq(rsc->pending_task, CRMD_ACTION_MIGRATE)) {
407         pending_state = "Migrating";
408 
409     } else if (safe_str_eq(rsc->pending_task, CRMD_ACTION_MIGRATED)) {
410        /* Work might be done in here. */
411         pending_state = "Migrating";
412 
413     } else if (safe_str_eq(rsc->pending_task, CRMD_ACTION_PROMOTE)) {
414         pending_state = "Promoting";
415 
416     } else if (safe_str_eq(rsc->pending_task, CRMD_ACTION_DEMOTE)) {
417         pending_state = "Demoting";
418     }
419 
420     return pending_state;
421 }
422 
423 static const char *
native_pending_task(resource_t * rsc)424 native_pending_task(resource_t * rsc)
425 {
426     const char *pending_task = NULL;
427 
428     if (safe_str_eq(rsc->pending_task, CRMD_ACTION_NOTIFY)) {
429         /* "Notifying" is not very useful to be shown. */
430         pending_task = NULL;
431 
432     } else if (safe_str_eq(rsc->pending_task, CRMD_ACTION_STATUS)) {
433         pending_task = "Monitoring";
434 
435     /* Pending probes are not printed, even if pending
436      * operations are requested. If someone ever requests that
437      * behavior, uncomment this and the corresponding part of
438      * unpack.c:unpack_rsc_op().
439      */
440     /*
441     } else if (safe_str_eq(rsc->pending_task, "probe")) {
442         pending_task = "Checking";
443     */
444     }
445 
446     return pending_task;
447 }
448 
449 static enum rsc_role_e
native_displayable_role(resource_t * rsc)450 native_displayable_role(resource_t *rsc)
451 {
452     enum rsc_role_e role = rsc->role;
453 
454     if ((role == RSC_ROLE_STARTED)
455         && (uber_parent(rsc)->variant == pe_master)) {
456 
457         role = RSC_ROLE_SLAVE;
458     }
459     return role;
460 }
461 
462 static const char *
native_displayable_state(resource_t * rsc,long options)463 native_displayable_state(resource_t *rsc, long options)
464 {
465     const char *rsc_state = NULL;
466 
467     if (options & pe_print_pending) {
468         rsc_state = native_pending_state(rsc);
469     }
470     if (rsc_state == NULL) {
471         rsc_state = role2text(native_displayable_role(rsc));
472     }
473     return rsc_state;
474 }
475 
476 static void
native_print_xml(resource_t * rsc,const char * pre_text,long options,void * print_data)477 native_print_xml(resource_t * rsc, const char *pre_text, long options, void *print_data)
478 {
479     const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
480     const char *prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
481     const char *rsc_state = native_displayable_state(rsc, options);
482     const char *target_role = NULL;
483 
484     /* resource information. */
485     status_print("%s<resource ", pre_text);
486     status_print("id=\"%s\" ", rsc_printable_id(rsc));
487     status_print("resource_agent=\"%s%s%s:%s\" ",
488                  class,
489                  prov ? "::" : "", prov ? prov : "", crm_element_value(rsc->xml, XML_ATTR_TYPE));
490 
491     status_print("role=\"%s\" ", rsc_state);
492     if (rsc->meta) {
493         target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
494     }
495     if (target_role) {
496         status_print("target_role=\"%s\" ", target_role);
497     }
498     status_print("active=\"%s\" ", rsc->fns->active(rsc, TRUE) ? "true" : "false");
499     status_print("orphaned=\"%s\" ", is_set(rsc->flags, pe_rsc_orphan) ? "true" : "false");
500     status_print("blocked=\"%s\" ", is_set(rsc->flags, pe_rsc_block) ? "true" : "false");
501     status_print("managed=\"%s\" ", is_set(rsc->flags, pe_rsc_managed) ? "true" : "false");
502     status_print("failed=\"%s\" ", is_set(rsc->flags, pe_rsc_failed) ? "true" : "false");
503     status_print("failure_ignored=\"%s\" ",
504                  is_set(rsc->flags, pe_rsc_failure_ignored) ? "true" : "false");
505     status_print("nodes_running_on=\"%d\" ", g_list_length(rsc->running_on));
506 
507     if (options & pe_print_pending) {
508         const char *pending_task = native_pending_task(rsc);
509 
510         if (pending_task) {
511             status_print("pending=\"%s\" ", pending_task);
512         }
513     }
514 
515     if (options & pe_print_dev) {
516         status_print("provisional=\"%s\" ",
517                      is_set(rsc->flags, pe_rsc_provisional) ? "true" : "false");
518         status_print("runnable=\"%s\" ", is_set(rsc->flags, pe_rsc_runnable) ? "true" : "false");
519         status_print("priority=\"%f\" ", (double)rsc->priority);
520         status_print("variant=\"%s\" ", crm_element_name(rsc->xml));
521     }
522 
523     /* print out the nodes this resource is running on */
524     if (options & pe_print_rsconly) {
525         status_print("/>\n");
526         /* do nothing */
527     } else if (rsc->running_on != NULL) {
528         GListPtr gIter = rsc->running_on;
529 
530         status_print(">\n");
531         for (; gIter != NULL; gIter = gIter->next) {
532             node_t *node = (node_t *) gIter->data;
533 
534             status_print("%s    <node name=\"%s\" id=\"%s\" cached=\"%s\"/>\n", pre_text,
535                          node->details->uname, node->details->id,
536                          node->details->online ? "false" : "true");
537         }
538         status_print("%s</resource>\n", pre_text);
539     } else {
540         status_print("/>\n");
541     }
542 }
543 
544 /* making this inline rather than a macro prevents a coverity "unreachable"
545  * warning on the first usage
546  */
547 static inline const char *
comma_if(int i)548 comma_if(int i)
549 {
550     return i? ", " : "";
551 }
552 
553 void
common_print(resource_t * rsc,const char * pre_text,const char * name,node_t * node,long options,void * print_data)554 common_print(resource_t * rsc, const char *pre_text, const char *name, node_t *node, long options, void *print_data)
555 {
556     const char *desc = NULL;
557     const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
558     const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE);
559     const char *target_role = NULL;
560     enum rsc_role_e role = native_displayable_role(rsc);
561 
562     int offset = 0;
563     int flagOffset = 0;
564     char buffer[LINE_MAX];
565     char flagBuffer[LINE_MAX];
566 
567     CRM_ASSERT(rsc->variant == pe_native);
568     CRM_ASSERT(kind != NULL);
569 
570     if (rsc->meta) {
571         const char *is_internal = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INTERNAL_RSC);
572         if (crm_is_true(is_internal) && is_not_set(options, pe_print_implicit)) {
573             crm_trace("skipping print of internal resource %s", rsc->id);
574             return;
575         }
576         target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
577     }
578 
579     if (pre_text == NULL && (options & pe_print_printf)) {
580         pre_text = " ";
581     }
582 
583     if (options & pe_print_xml) {
584         native_print_xml(rsc, pre_text, options, print_data);
585         return;
586     }
587 
588     if ((options & pe_print_rsconly) || g_list_length(rsc->running_on) > 1) {
589         node = NULL;
590     }
591 
592     if (options & pe_print_html) {
593         if (is_not_set(rsc->flags, pe_rsc_managed)) {
594             status_print("<font color=\"yellow\">");
595 
596         } else if (is_set(rsc->flags, pe_rsc_failed)) {
597             status_print("<font color=\"red\">");
598 
599         } else if (rsc->variant == pe_native && (rsc->running_on == NULL)) {
600             status_print("<font color=\"red\">");
601 
602         } else if (g_list_length(rsc->running_on) > 1) {
603             status_print("<font color=\"orange\">");
604 
605         } else if (is_set(rsc->flags, pe_rsc_failure_ignored)) {
606             status_print("<font color=\"yellow\">");
607 
608         } else {
609             status_print("<font color=\"green\">");
610         }
611     }
612 
613     if(pre_text) {
614         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", pre_text);
615     }
616     offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", name);
617     offset += snprintf(buffer + offset, LINE_MAX - offset, "\t(%s", class);
618     if (is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
619         const char *prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
620         offset += snprintf(buffer + offset, LINE_MAX - offset, "::%s", prov);
621     }
622     offset += snprintf(buffer + offset, LINE_MAX - offset, ":%s):\t", kind);
623     if(is_set(rsc->flags, pe_rsc_orphan)) {
624         offset += snprintf(buffer + offset, LINE_MAX - offset, " ORPHANED ");
625     }
626     if(role > RSC_ROLE_SLAVE && is_set(rsc->flags, pe_rsc_failed)) {
627         offset += snprintf(buffer + offset, LINE_MAX - offset, "FAILED %s", role2text(role));
628     } else if(is_set(rsc->flags, pe_rsc_failed)) {
629         offset += snprintf(buffer + offset, LINE_MAX - offset, "FAILED");
630     } else {
631         const char *rsc_state = native_displayable_state(rsc, options);
632 
633         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", rsc_state);
634     }
635 
636     if(node) {
637         offset += snprintf(buffer + offset, LINE_MAX - offset, " %s", node->details->uname);
638 
639         if (node->details->online == FALSE && node->details->unclean) {
640             flagOffset += snprintf(flagBuffer + flagOffset, LINE_MAX - flagOffset,
641                                    "%sUNCLEAN", comma_if(flagOffset));
642         }
643     }
644 
645     if (options & pe_print_pending) {
646         const char *pending_task = native_pending_task(rsc);
647 
648         if (pending_task) {
649             flagOffset += snprintf(flagBuffer + flagOffset, LINE_MAX - flagOffset,
650                                    "%s%s", comma_if(flagOffset), pending_task);
651         }
652     }
653 
654     if (target_role) {
655         enum rsc_role_e target_role_e = text2role(target_role);
656 
657         /* Ignore target role Started, as it is the default anyways
658          * (and would also allow a Master to be Master).
659          * Show if target role limits our abilities. */
660         if (target_role_e == RSC_ROLE_STOPPED) {
661             flagOffset += snprintf(flagBuffer + flagOffset, LINE_MAX - flagOffset,
662                                    "%sdisabled", comma_if(flagOffset));
663 
664         } else if (uber_parent(rsc)->variant == pe_master
665                    && target_role_e == RSC_ROLE_SLAVE) {
666             flagOffset += snprintf(flagBuffer + flagOffset, LINE_MAX - flagOffset,
667                                    "%starget-role:%s", comma_if(flagOffset), target_role);
668         }
669     }
670 
671     if (is_set(rsc->flags, pe_rsc_block)) {
672         flagOffset += snprintf(flagBuffer + flagOffset, LINE_MAX - flagOffset,
673                                "%sblocked", comma_if(flagOffset));
674 
675     } else if (is_not_set(rsc->flags, pe_rsc_managed)) {
676         flagOffset += snprintf(flagBuffer + flagOffset, LINE_MAX - flagOffset,
677                                "%sunmanaged", comma_if(flagOffset));
678     }
679 
680     if(is_set(rsc->flags, pe_rsc_failure_ignored)) {
681         flagOffset += snprintf(flagBuffer + flagOffset, LINE_MAX - flagOffset,
682                                "%sfailure ignored", comma_if(flagOffset));
683     }
684 
685     if ((options & pe_print_rsconly) || g_list_length(rsc->running_on) > 1) {
686         desc = crm_element_value(rsc->xml, XML_ATTR_DESC);
687     }
688 
689     CRM_LOG_ASSERT(offset > 0);
690     if(flagOffset > 0) {
691         status_print("%s (%s)%s%s", buffer, flagBuffer, desc?" ":"", desc?desc:"");
692     } else {
693         status_print("%s%s%s", buffer, desc?" ":"", desc?desc:"");
694     }
695 
696 #if CURSES_ENABLED
697     if ((options & pe_print_rsconly) || g_list_length(rsc->running_on) > 1) {
698         /* Done */
699 
700     } else if (options & pe_print_ncurses) {
701         /* coverity[negative_returns] False positive */
702         move(-1, 0);
703     }
704 #endif
705 
706     if (options & pe_print_html) {
707         status_print(" </font> ");
708     }
709 
710     if ((options & pe_print_rsconly)) {
711 
712     } else if (g_list_length(rsc->running_on) > 1) {
713         GListPtr gIter = rsc->running_on;
714         int counter = 0;
715 
716         if (options & pe_print_html) {
717             status_print("<ul>\n");
718         } else if ((options & pe_print_printf)
719                    || (options & pe_print_ncurses)) {
720             status_print("[");
721         }
722 
723         for (; gIter != NULL; gIter = gIter->next) {
724             node_t *n = (node_t *) gIter->data;
725 
726             counter++;
727 
728             if (options & pe_print_html) {
729                 status_print("<li>\n%s", n->details->uname);
730 
731             } else if ((options & pe_print_printf)
732                        || (options & pe_print_ncurses)) {
733                 status_print(" %s", n->details->uname);
734 
735             } else if ((options & pe_print_log)) {
736                 status_print("\t%d : %s", counter, n->details->uname);
737 
738             } else {
739                 status_print("%s", n->details->uname);
740             }
741             if (options & pe_print_html) {
742                 status_print("</li>\n");
743 
744             }
745         }
746 
747         if (options & pe_print_html) {
748             status_print("</ul>\n");
749         } else if ((options & pe_print_printf)
750                    || (options & pe_print_ncurses)) {
751             status_print(" ]");
752         }
753     }
754 
755     if (options & pe_print_html) {
756         status_print("<br/>\n");
757     } else if (options & pe_print_suppres_nl) {
758         /* nothing */
759     } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
760         status_print("\n");
761     }
762 
763     if (options & pe_print_details) {
764         struct print_data_s pdata;
765 
766         pdata.options = options;
767         pdata.print_data = print_data;
768         g_hash_table_foreach(rsc->parameters, native_print_attr, &pdata);
769     }
770 
771     if (options & pe_print_dev) {
772         GHashTableIter iter;
773         node_t *n = NULL;
774 
775         status_print("%s\t(%s%svariant=%s, priority=%f)", pre_text,
776                      is_set(rsc->flags, pe_rsc_provisional) ? "provisional, " : "",
777                      is_set(rsc->flags, pe_rsc_runnable) ? "" : "non-startable, ",
778                      crm_element_name(rsc->xml), (double)rsc->priority);
779         status_print("%s\tAllowed Nodes", pre_text);
780         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
781         while (g_hash_table_iter_next(&iter, NULL, (void **)&n)) {
782             status_print("%s\t * %s %d", pre_text, n->details->uname, n->weight);
783         }
784     }
785 
786     if (options & pe_print_max_details) {
787         GHashTableIter iter;
788         node_t *n = NULL;
789 
790         status_print("%s\t=== Allowed Nodes\n", pre_text);
791         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
792         while (g_hash_table_iter_next(&iter, NULL, (void **)&n)) {
793             print_node("\t", n, FALSE);
794         }
795     }
796 }
797 
798 void
native_print(resource_t * rsc,const char * pre_text,long options,void * print_data)799 native_print(resource_t * rsc, const char *pre_text, long options, void *print_data)
800 {
801     node_t *node = NULL;
802 
803     CRM_ASSERT(rsc->variant == pe_native);
804     if (options & pe_print_xml) {
805         native_print_xml(rsc, pre_text, options, print_data);
806         return;
807     }
808 
809     node = pe__current_node(rsc);
810 
811     if (node == NULL) {
812         // This is set only if a non-probe action is pending on this node
813         node = rsc->pending_node;
814     }
815 
816     common_print(rsc, pre_text, rsc_printable_id(rsc), node, options, print_data);
817 }
818 
819 void
native_free(resource_t * rsc)820 native_free(resource_t * rsc)
821 {
822     pe_rsc_trace(rsc, "Freeing resource action list (not the data)");
823     common_free(rsc);
824 }
825 
826 enum rsc_role_e
native_resource_state(const resource_t * rsc,gboolean current)827 native_resource_state(const resource_t * rsc, gboolean current)
828 {
829     enum rsc_role_e role = rsc->next_role;
830 
831     if (current) {
832         role = rsc->role;
833     }
834     pe_rsc_trace(rsc, "%s state: %s", rsc->id, role2text(role));
835     return role;
836 }
837 
838 /*!
839  * \internal
840  * \brief List nodes where a resource (or any of its children) is
841  *
842  * \param[in]  rsc      Resource to check
843  * \param[out] list     List to add result to
844  * \param[in]  current  0 = where known, 1 = running, 2 = running or pending
845  *
846  * \return If list contains only one node, that node
847  */
848 pe_node_t *
native_location(const pe_resource_t * rsc,GList ** list,int current)849 native_location(const pe_resource_t *rsc, GList **list, int current)
850 {
851     node_t *one = NULL;
852     GListPtr result = NULL;
853 
854     if (rsc->children) {
855         GListPtr gIter = rsc->children;
856 
857         for (; gIter != NULL; gIter = gIter->next) {
858             resource_t *child = (resource_t *) gIter->data;
859 
860             child->fns->location(child, &result, current);
861         }
862 
863     } else if (current) {
864 
865         if (rsc->running_on) {
866             result = g_list_copy(rsc->running_on);
867         }
868         if ((current == 2) && rsc->pending_node
869             && !pe_find_node_id(result, rsc->pending_node->details->id)) {
870                 result = g_list_append(result, rsc->pending_node);
871         }
872 
873     } else if (current == FALSE && rsc->allocated_to) {
874         result = g_list_append(NULL, rsc->allocated_to);
875     }
876 
877     if (result && (result->next == NULL)) {
878         one = result->data;
879     }
880 
881     if (list) {
882         GListPtr gIter = result;
883 
884         for (; gIter != NULL; gIter = gIter->next) {
885             node_t *node = (node_t *) gIter->data;
886 
887             if (*list == NULL || pe_find_node_id(*list, node->details->id) == NULL) {
888                 *list = g_list_append(*list, node);
889             }
890         }
891     }
892 
893     g_list_free(result);
894     return one;
895 }
896 
897 static void
get_rscs_brief(GListPtr rsc_list,GHashTable * rsc_table,GHashTable * active_table)898 get_rscs_brief(GListPtr rsc_list, GHashTable * rsc_table, GHashTable * active_table)
899 {
900     GListPtr gIter = rsc_list;
901 
902     for (; gIter != NULL; gIter = gIter->next) {
903         resource_t *rsc = (resource_t *) gIter->data;
904 
905         const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
906         const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE);
907 
908         int offset = 0;
909         char buffer[LINE_MAX];
910 
911         int *rsc_counter = NULL;
912         int *active_counter = NULL;
913 
914         if (rsc->variant != pe_native) {
915             continue;
916         }
917 
918         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", class);
919         if (is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
920             const char *prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
921             offset += snprintf(buffer + offset, LINE_MAX - offset, "::%s", prov);
922         }
923         offset += snprintf(buffer + offset, LINE_MAX - offset, ":%s", kind);
924         CRM_LOG_ASSERT(offset > 0);
925 
926         if (rsc_table) {
927             rsc_counter = g_hash_table_lookup(rsc_table, buffer);
928             if (rsc_counter == NULL) {
929                 rsc_counter = calloc(1, sizeof(int));
930                 *rsc_counter = 0;
931                 g_hash_table_insert(rsc_table, strdup(buffer), rsc_counter);
932             }
933             (*rsc_counter)++;
934         }
935 
936         if (active_table) {
937             GListPtr gIter2 = rsc->running_on;
938 
939             for (; gIter2 != NULL; gIter2 = gIter2->next) {
940                 node_t *node = (node_t *) gIter2->data;
941                 GHashTable *node_table = NULL;
942 
943                 if (node->details->unclean == FALSE && node->details->online == FALSE) {
944                     continue;
945                 }
946 
947                 node_table = g_hash_table_lookup(active_table, node->details->uname);
948                 if (node_table == NULL) {
949                     node_table = crm_str_table_new();
950                     g_hash_table_insert(active_table, strdup(node->details->uname), node_table);
951                 }
952 
953                 active_counter = g_hash_table_lookup(node_table, buffer);
954                 if (active_counter == NULL) {
955                     active_counter = calloc(1, sizeof(int));
956                     *active_counter = 0;
957                     g_hash_table_insert(node_table, strdup(buffer), active_counter);
958                 }
959                 (*active_counter)++;
960             }
961         }
962     }
963 }
964 
965 static void
destroy_node_table(gpointer data)966 destroy_node_table(gpointer data)
967 {
968     GHashTable *node_table = data;
969 
970     if (node_table) {
971         g_hash_table_destroy(node_table);
972     }
973 }
974 
975 void
print_rscs_brief(GListPtr rsc_list,const char * pre_text,long options,void * print_data,gboolean print_all)976 print_rscs_brief(GListPtr rsc_list, const char *pre_text, long options,
977                  void *print_data, gboolean print_all)
978 {
979     GHashTable *rsc_table = crm_str_table_new();
980     GHashTable *active_table = g_hash_table_new_full(crm_str_hash, g_str_equal,
981                                                      free, destroy_node_table);
982     GHashTableIter hash_iter;
983     char *type = NULL;
984     int *rsc_counter = NULL;
985 
986     get_rscs_brief(rsc_list, rsc_table, active_table);
987 
988     g_hash_table_iter_init(&hash_iter, rsc_table);
989     while (g_hash_table_iter_next(&hash_iter, (gpointer *)&type, (gpointer *)&rsc_counter)) {
990         GHashTableIter hash_iter2;
991         char *node_name = NULL;
992         GHashTable *node_table = NULL;
993         int active_counter_all = 0;
994 
995         g_hash_table_iter_init(&hash_iter2, active_table);
996         while (g_hash_table_iter_next(&hash_iter2, (gpointer *)&node_name, (gpointer *)&node_table)) {
997             int *active_counter = g_hash_table_lookup(node_table, type);
998 
999             if (active_counter == NULL || *active_counter == 0) {
1000                 continue;
1001 
1002             } else {
1003                 active_counter_all += *active_counter;
1004             }
1005 
1006             if (options & pe_print_rsconly) {
1007                 node_name = NULL;
1008             }
1009 
1010             if (options & pe_print_html) {
1011                 status_print("<li>\n");
1012             }
1013 
1014             if (print_all) {
1015                 status_print("%s%d/%d\t(%s):\tActive %s\n", pre_text ? pre_text : "",
1016                              active_counter ? *active_counter : 0,
1017                              rsc_counter ? *rsc_counter : 0, type,
1018                              active_counter && (*active_counter > 0) && node_name ? node_name : "");
1019             } else {
1020                 status_print("%s%d\t(%s):\tActive %s\n", pre_text ? pre_text : "",
1021                              active_counter ? *active_counter : 0, type,
1022                              active_counter && (*active_counter > 0) && node_name ? node_name : "");
1023             }
1024 
1025             if (options & pe_print_html) {
1026                 status_print("</li>\n");
1027             }
1028         }
1029 
1030         if (print_all && active_counter_all == 0) {
1031             if (options & pe_print_html) {
1032                 status_print("<li>\n");
1033             }
1034 
1035             status_print("%s%d/%d\t(%s):\tActive\n", pre_text ? pre_text : "",
1036                          active_counter_all,
1037                          rsc_counter ? *rsc_counter : 0, type);
1038 
1039             if (options & pe_print_html) {
1040                 status_print("</li>\n");
1041             }
1042         }
1043     }
1044 
1045     if (rsc_table) {
1046         g_hash_table_destroy(rsc_table);
1047         rsc_table = NULL;
1048     }
1049     if (active_table) {
1050         g_hash_table_destroy(active_table);
1051         active_table = NULL;
1052     }
1053 }
1054