diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h index 2c532b48e6..189ba7ba58 100644 --- a/include/crm/pengine/internal.h +++ b/include/crm/pengine/internal.h @@ -1,473 +1,476 @@ /* * Copyright 2004-2020 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PE_INTERNAL__H # define PE_INTERNAL__H # include # include # include # include # define pe_rsc_info(rsc, fmt, args...) crm_log_tag(LOG_INFO, rsc ? rsc->id : "", fmt, ##args) # define pe_rsc_debug(rsc, fmt, args...) crm_log_tag(LOG_DEBUG, rsc ? rsc->id : "", fmt, ##args) # define pe_rsc_trace(rsc, fmt, args...) crm_log_tag(LOG_TRACE, rsc ? rsc->id : "", fmt, ##args) # define pe_err(fmt...) { was_processing_error = TRUE; crm_config_error = TRUE; crm_err(fmt); } # define pe_warn(fmt...) { was_processing_warning = TRUE; crm_config_warning = TRUE; crm_warn(fmt); } # define pe_proc_err(fmt...) { was_processing_error = TRUE; crm_err(fmt); } # define pe_proc_warn(fmt...) { was_processing_warning = TRUE; crm_warn(fmt); } # define pe_set_action_bit(action, bit) action->flags = crm_set_bit(__FUNCTION__, __LINE__, action->uuid, action->flags, bit) # define pe_clear_action_bit(action, bit) action->flags = crm_clear_bit(__FUNCTION__, __LINE__, action->uuid, action->flags, bit) // Some warnings we don't want to print every transition enum pe_warn_once_e { pe_wo_blind = 0x0001, pe_wo_restart_type = 0x0002, pe_wo_role_after = 0x0004, pe_wo_poweroff = 0x0008, pe_wo_require_all = 0x0010, pe_wo_order_score = 0x0020, pe_wo_neg_threshold = 0x0040, }; extern uint32_t pe_wo; #define pe_warn_once(pe_wo_bit, fmt...) do { \ if (is_not_set(pe_wo, pe_wo_bit)) { \ if (pe_wo_bit == pe_wo_blind) { \ crm_warn(fmt); \ } else { \ pe_warn(fmt); \ } \ set_bit(pe_wo, pe_wo_bit); \ } \ } while (0); typedef struct pe__location_constraint_s { char *id; // Constraint XML ID pe_resource_t *rsc_lh; // Resource being located enum rsc_role_e role_filter; // Role to locate enum pe_discover_e discover_mode; // Resource discovery GListPtr node_list_rh; // List of pe_node_t* } pe__location_t; typedef struct pe__order_constraint_s { int id; enum pe_ordering type; void *lh_opaque; pe_resource_t *lh_rsc; pe_action_t *lh_action; char *lh_action_task; void *rh_opaque; pe_resource_t *rh_rsc; pe_action_t *rh_action; char *rh_action_task; } pe__ordering_t; typedef struct notify_data_s { GSList *keys; // Environment variable name/value pairs const char *action; pe_action_t *pre; pe_action_t *post; pe_action_t *pre_done; pe_action_t *post_done; GListPtr active; /* notify_entry_t* */ GListPtr inactive; /* notify_entry_t* */ GListPtr start; /* notify_entry_t* */ GListPtr stop; /* notify_entry_t* */ GListPtr demote; /* notify_entry_t* */ GListPtr promote; /* notify_entry_t* */ GListPtr master; /* notify_entry_t* */ GListPtr slave; /* notify_entry_t* */ GHashTable *allowed_nodes; } notify_data_t; bool pe_can_fence(pe_working_set_t *data_set, pe_node_t *node); int pe__add_scores(int score1, int score2); void add_hash_param(GHashTable * hash, const char *name, const char *value); char *native_parameter(pe_resource_t * rsc, pe_node_t * node, gboolean create, const char *name, pe_working_set_t * data_set); pe_node_t *native_location(const pe_resource_t *rsc, GList **list, int current); void pe_metadata(void); void verify_pe_options(GHashTable * options); void common_update_score(pe_resource_t * rsc, const char *id, int score); void native_add_running(pe_resource_t * rsc, pe_node_t * node, pe_working_set_t * data_set); gboolean native_unpack(pe_resource_t * rsc, pe_working_set_t * data_set); gboolean group_unpack(pe_resource_t * rsc, pe_working_set_t * data_set); gboolean clone_unpack(pe_resource_t * rsc, pe_working_set_t * data_set); gboolean pe__unpack_bundle(pe_resource_t *rsc, pe_working_set_t *data_set); pe_resource_t *native_find_rsc(pe_resource_t *rsc, const char *id, const pe_node_t *node, int flags); gboolean native_active(pe_resource_t * rsc, gboolean all); gboolean group_active(pe_resource_t * rsc, gboolean all); gboolean clone_active(pe_resource_t * rsc, gboolean all); gboolean pe__bundle_active(pe_resource_t *rsc, gboolean all); void native_print(pe_resource_t * rsc, const char *pre_text, long options, void *print_data); void group_print(pe_resource_t * rsc, const char *pre_text, long options, void *print_data); void clone_print(pe_resource_t * rsc, const char *pre_text, long options, void *print_data); void pe__print_bundle(pe_resource_t *rsc, const char *pre_text, long options, void *print_data); int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name , size_t pairs_count, ...); char *pe__node_display_name(pe_node_t *node, bool print_detail); int pe__ban_html(pcmk__output_t *out, va_list args); int pe__ban_text(pcmk__output_t *out, va_list args); int pe__ban_xml(pcmk__output_t *out, va_list args); int pe__clone_xml(pcmk__output_t *out, va_list args); int pe__clone_html(pcmk__output_t *out, va_list args); int pe__clone_text(pcmk__output_t *out, va_list args); int pe__cluster_counts_html(pcmk__output_t *out, va_list args); int pe__cluster_counts_text(pcmk__output_t *out, va_list args); int pe__cluster_counts_xml(pcmk__output_t *out, va_list args); int pe__cluster_dc_html(pcmk__output_t *out, va_list args); int pe__cluster_dc_text(pcmk__output_t *out, va_list args); int pe__cluster_dc_xml(pcmk__output_t *out, va_list args); int pe__cluster_maint_mode_html(pcmk__output_t *out, va_list args); int pe__cluster_maint_mode_text(pcmk__output_t *out, va_list args); int pe__cluster_options_html(pcmk__output_t *out, va_list args); int pe__cluster_options_log(pcmk__output_t *out, va_list args); int pe__cluster_options_text(pcmk__output_t *out, va_list args); int pe__cluster_options_xml(pcmk__output_t *out, va_list args); int pe__cluster_stack_html(pcmk__output_t *out, va_list args); int pe__cluster_stack_text(pcmk__output_t *out, va_list args); int pe__cluster_stack_xml(pcmk__output_t *out, va_list args); int pe__cluster_summary(pcmk__output_t *out, va_list args); int pe__cluster_summary_html(pcmk__output_t *out, va_list args); int pe__cluster_times_html(pcmk__output_t *out, va_list args); int pe__cluster_times_xml(pcmk__output_t *out, va_list args); int pe__cluster_times_text(pcmk__output_t *out, va_list args); int pe__failed_action_text(pcmk__output_t *out, va_list args); int pe__failed_action_xml(pcmk__output_t *out, va_list args); int pe__group_xml(pcmk__output_t *out, va_list args); int pe__group_html(pcmk__output_t *out, va_list args); int pe__group_text(pcmk__output_t *out, va_list args); int pe__bundle_xml(pcmk__output_t *out, va_list args); int pe__bundle_html(pcmk__output_t *out, va_list args); int pe__bundle_text(pcmk__output_t *out, va_list args); int pe__node_html(pcmk__output_t *out, va_list args); int pe__node_text(pcmk__output_t *out, va_list args); int pe__node_xml(pcmk__output_t *out, va_list args); int pe__node_attribute_html(pcmk__output_t *out, va_list args); int pe__node_attribute_text(pcmk__output_t *out, va_list args); int pe__node_attribute_xml(pcmk__output_t *out, va_list args); +int pe__node_list_html(pcmk__output_t *out, va_list args); +int pe__node_list_text(pcmk__output_t *out, va_list args); +int pe__node_list_xml(pcmk__output_t *out, va_list args); int pe__op_history_text(pcmk__output_t *out, va_list args); int pe__op_history_xml(pcmk__output_t *out, va_list args); int pe__resource_history_text(pcmk__output_t *out, va_list args); int pe__resource_history_xml(pcmk__output_t *out, va_list args); int pe__resource_xml(pcmk__output_t *out, va_list args); int pe__resource_html(pcmk__output_t *out, va_list args); int pe__resource_text(pcmk__output_t *out, va_list args); int pe__ticket_html(pcmk__output_t *out, va_list args); int pe__ticket_text(pcmk__output_t *out, va_list args); int pe__ticket_xml(pcmk__output_t *out, va_list args); void native_free(pe_resource_t * rsc); void group_free(pe_resource_t * rsc); void clone_free(pe_resource_t * rsc); void pe__free_bundle(pe_resource_t *rsc); enum rsc_role_e native_resource_state(const pe_resource_t * rsc, gboolean current); enum rsc_role_e group_resource_state(const pe_resource_t * rsc, gboolean current); enum rsc_role_e clone_resource_state(const pe_resource_t * rsc, gboolean current); enum rsc_role_e pe__bundle_resource_state(const pe_resource_t *rsc, gboolean current); void pe__count_common(pe_resource_t *rsc); void pe__count_bundle(pe_resource_t *rsc); gboolean common_unpack(xmlNode * xml_obj, pe_resource_t ** rsc, pe_resource_t * parent, pe_working_set_t * data_set); void common_free(pe_resource_t * rsc); pe_node_t *pe__copy_node(const pe_node_t *this_node); extern time_t get_effective_time(pe_working_set_t * data_set); /* Failure handling utilities (from failcounts.c) */ // bit flags for fail count handling options enum pe_fc_flags_e { pe_fc_default = 0x00, pe_fc_effective = 0x01, // don't count expired failures pe_fc_fillers = 0x02, // if container, include filler failures in count }; int pe_get_failcount(pe_node_t *node, pe_resource_t *rsc, time_t *last_failure, uint32_t flags, xmlNode *xml_op, pe_working_set_t *data_set); pe_action_t *pe__clear_failcount(pe_resource_t *rsc, pe_node_t *node, const char *reason, pe_working_set_t *data_set); /* Functions for finding/counting a resource's active nodes */ pe_node_t *pe__find_active_on(const pe_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean); pe_node_t *pe__find_active_requires(const pe_resource_t *rsc, unsigned int *count); static inline pe_node_t * pe__current_node(const pe_resource_t *rsc) { return pe__find_active_on(rsc, NULL, NULL); } /* Binary like operators for lists of nodes */ extern void node_list_exclude(GHashTable * list, GListPtr list2, gboolean merge_scores); GHashTable *pe__node_list2table(GList *list); static inline gpointer pe_hash_table_lookup(GHashTable * hash, gconstpointer key) { if (hash) { return g_hash_table_lookup(hash, key); } return NULL; } extern pe_action_t *get_pseudo_op(const char *name, pe_working_set_t * data_set); extern gboolean order_actions(pe_action_t * lh_action, pe_action_t * rh_action, enum pe_ordering order); /* Printing functions for debug */ extern void print_node(const char *pre_text, pe_node_t * node, gboolean details); extern void print_str_str(gpointer key, gpointer value, gpointer user_data); extern void pe__output_node(pe_node_t * node, gboolean details, pcmk__output_t *out); extern void dump_node_capacity(int level, const char *comment, pe_node_t * node); extern void dump_rsc_utilization(int level, const char *comment, pe_resource_t * rsc, pe_node_t * node); 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); #define pe__show_node_weights(level, rsc, text, nodes) \ pe__show_node_weights_as(__FILE__, __FUNCTION__, __LINE__, \ (level), (rsc), (text), (nodes)) /* Sorting functions */ extern gint sort_rsc_priority(gconstpointer a, gconstpointer b); extern gint sort_rsc_index(gconstpointer a, gconstpointer b); extern xmlNode *find_rsc_op_entry(pe_resource_t * rsc, const char *key); extern pe_action_t *custom_action(pe_resource_t * rsc, char *key, const char *task, pe_node_t * on_node, gboolean optional, gboolean foo, pe_working_set_t * data_set); # define delete_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_DELETE, 0) # define delete_action(rsc, node, optional) custom_action( \ rsc, delete_key(rsc), CRMD_ACTION_DELETE, node, \ optional, TRUE, data_set); # define stopped_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_STOPPED, 0) # define stopped_action(rsc, node, optional) custom_action( \ rsc, stopped_key(rsc), CRMD_ACTION_STOPPED, node, \ optional, TRUE, data_set); # define stop_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_STOP, 0) # define stop_action(rsc, node, optional) custom_action( \ rsc, stop_key(rsc), CRMD_ACTION_STOP, node, \ optional, TRUE, data_set); # define reload_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_RELOAD, 0) # define start_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_START, 0) # define start_action(rsc, node, optional) custom_action( \ rsc, start_key(rsc), CRMD_ACTION_START, node, \ optional, TRUE, data_set) # define started_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_STARTED, 0) # define started_action(rsc, node, optional) custom_action( \ rsc, started_key(rsc), CRMD_ACTION_STARTED, node, \ optional, TRUE, data_set) # define promote_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_PROMOTE, 0) # define promote_action(rsc, node, optional) custom_action( \ rsc, promote_key(rsc), CRMD_ACTION_PROMOTE, node, \ optional, TRUE, data_set) # define promoted_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_PROMOTED, 0) # define promoted_action(rsc, node, optional) custom_action( \ rsc, promoted_key(rsc), CRMD_ACTION_PROMOTED, node, \ optional, TRUE, data_set) # define demote_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_DEMOTE, 0) # define demote_action(rsc, node, optional) custom_action( \ rsc, demote_key(rsc), CRMD_ACTION_DEMOTE, node, \ optional, TRUE, data_set) # define demoted_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_DEMOTED, 0) # define demoted_action(rsc, node, optional) custom_action( \ rsc, demoted_key(rsc), CRMD_ACTION_DEMOTED, node, \ optional, TRUE, data_set) extern int pe_get_configured_timeout(pe_resource_t *rsc, const char *action, pe_working_set_t *data_set); extern pe_action_t *find_first_action(GListPtr input, const char *uuid, const char *task, pe_node_t * on_node); extern enum action_tasks get_complex_task(pe_resource_t * rsc, const char *name, gboolean allow_non_atomic); extern GListPtr find_actions(GListPtr input, const char *key, const pe_node_t *on_node); GList *find_actions_exact(GList *input, const char *key, const pe_node_t *on_node); extern GListPtr find_recurring_actions(GListPtr input, pe_node_t * not_on_node); GList *pe__resource_actions(const pe_resource_t *rsc, const pe_node_t *node, const char *task, bool require_node); extern void pe_free_action(pe_action_t * action); extern void resource_location(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag, pe_working_set_t * data_set); extern gint sort_op_by_callid(gconstpointer a, gconstpointer b); extern gboolean get_target_role(pe_resource_t * rsc, enum rsc_role_e *role); extern pe_resource_t *find_clone_instance(pe_resource_t * rsc, const char *sub_id, pe_working_set_t * data_set); extern void destroy_ticket(gpointer data); extern pe_ticket_t *ticket_new(const char *ticket_id, pe_working_set_t * data_set); // Resources for manipulating resource names const char *pe_base_name_end(const char *id); char *clone_strip(const char *last_rsc_id); char *clone_zero(const char *last_rsc_id); static inline bool pe_base_name_eq(pe_resource_t *rsc, const char *id) { if (id && rsc && rsc->id) { // Number of characters in rsc->id before any clone suffix size_t base_len = pe_base_name_end(rsc->id) - rsc->id + 1; return (strlen(id) == base_len) && !strncmp(id, rsc->id, base_len); } return FALSE; } int pe__target_rc_from_xml(xmlNode *xml_op); gint sort_node_uname(gconstpointer a, gconstpointer b); bool is_set_recursive(pe_resource_t * rsc, long long flag, bool any); enum rsc_digest_cmp_val { /*! Digests are the same */ RSC_DIGEST_MATCH = 0, /*! Params that require a restart changed */ RSC_DIGEST_RESTART, /*! Some parameter changed. */ RSC_DIGEST_ALL, /*! rsc op didn't have a digest associated with it, so * it is unknown if parameters changed or not. */ RSC_DIGEST_UNKNOWN, }; typedef struct op_digest_cache_s { enum rsc_digest_cmp_val rc; xmlNode *params_all; xmlNode *params_secure; xmlNode *params_restart; char *digest_all_calc; char *digest_secure_calc; char *digest_restart_calc; } op_digest_cache_t; op_digest_cache_t *rsc_action_digest_cmp(pe_resource_t * rsc, xmlNode * xml_op, pe_node_t * node, pe_working_set_t * data_set); 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); void trigger_unfencing( pe_resource_t * rsc, pe_node_t *node, const char *reason, pe_action_t *dependency, pe_working_set_t * data_set); void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite); void 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); #define pe_action_required(action, reason, text) pe_action_set_flag_reason(__FUNCTION__, __LINE__, action, reason, text, pe_action_optional, FALSE) #define pe_action_implies(action, reason, flag) pe_action_set_flag_reason(__FUNCTION__, __LINE__, action, reason, NULL, flag, FALSE) void set_bit_recursive(pe_resource_t * rsc, unsigned long long flag); void clear_bit_recursive(pe_resource_t * rsc, unsigned long long flag); gboolean add_tag_ref(GHashTable * tags, const char * tag_name, const char * obj_ref); void print_rscs_brief(GListPtr rsc_list, const char * pre_text, long options, void * print_data, gboolean print_all); int pe__rscs_brief_output(pcmk__output_t *out, GListPtr rsc_list, long options, gboolean print_all); void pe_fence_node(pe_working_set_t * data_set, pe_node_t * node, const char *reason, bool priority_delay); pe_node_t *pe_create_node(const char *id, const char *uname, const char *type, const char *score, pe_working_set_t * data_set); void common_print(pe_resource_t * rsc, const char *pre_text, const char *name, pe_node_t *node, long options, void *print_data); int pe__common_output_text(pcmk__output_t *out, pe_resource_t * rsc, const char *name, pe_node_t *node, long options); int pe__common_output_html(pcmk__output_t *out, pe_resource_t * rsc, const char *name, pe_node_t *node, long options); pe_resource_t *pe__find_bundle_replica(const pe_resource_t *bundle, const pe_node_t *node); bool pe__bundle_needs_remote_name(pe_resource_t *rsc); const char *pe__add_bundle_remote_name(pe_resource_t *rsc, xmlNode *xml, const char *field); const char *pe_node_attribute_calculated(const pe_node_t *node, const char *name, const pe_resource_t *rsc); const char *pe_node_attribute_raw(pe_node_t *node, const char *name); bool pe__is_universal_clone(pe_resource_t *rsc, pe_working_set_t *data_set); void pe__add_param_check(xmlNode *rsc_op, pe_resource_t *rsc, pe_node_t *node, enum pe_check_parameters, pe_working_set_t *data_set); void pe__foreach_param_check(pe_working_set_t *data_set, void (*cb)(pe_resource_t*, pe_node_t*, xmlNode*, enum pe_check_parameters, pe_working_set_t*)); void pe__free_param_checks(pe_working_set_t *data_set); bool pe__shutdown_requested(pe_node_t *node); void pe__update_recheck_time(time_t recheck, pe_working_set_t *data_set); #define BOOL2STR(x) ((x) ? "true" : "false") /*! * \internal * \brief Register xml formatting message functions. */ void pe__register_messages(pcmk__output_t *out); void pe__unpack_dataset_nvpairs(xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, pe_working_set_t *data_set); bool pe__resource_is_disabled(pe_resource_t *rsc); pe_action_t *pe__clear_resource_history(pe_resource_t *rsc, pe_node_t *node, pe_working_set_t *data_set); GListPtr pe__unames_with_tag(pe_working_set_t *data_set, const char *tag_name); bool pe__uname_has_tag(pe_working_set_t *data_set, const char *node, const char *tag); bool pe__rsc_running_on_any_node_in_list(GListPtr rsc_nodes, GListPtr node_list); #endif diff --git a/lib/pengine/pe_output.c b/lib/pengine/pe_output.c index ec3093db64..dd87955172 100644 --- a/lib/pengine/pe_output.c +++ b/lib/pengine/pe_output.c @@ -1,1552 +1,1753 @@ /* * Copyright 2019-2020 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #define SUMMARY_HEADER(rc, out) do { \ if (rc == pcmk_rc_no_output) { \ out->begin_list(out, NULL, NULL, "Cluster Summary"); \ rc = pcmk_rc_ok; \ } \ } while (0) static char * failed_action_string(xmlNodePtr xml_op) { const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY); int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0"); int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0"); const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON); time_t last_change = 0; if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, &last_change) == pcmk_ok) { crm_time_t *crm_when = crm_time_new(NULL); char *time_s = NULL; char *buf = NULL; crm_time_set_timet(crm_when, &last_change); time_s = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone); buf = crm_strdup_printf("%s on %s '%s' (%d): call=%s, status='%s', " "exitreason='%s', " XML_RSC_OP_LAST_CHANGE "='%s', queued=%sms, exec=%sms", op_key ? op_key : ID(xml_op), crm_element_value(xml_op, XML_ATTR_UNAME), services_ocf_exitcode_str(rc), rc, crm_element_value(xml_op, XML_LRM_ATTR_CALLID), services_lrm_status_str(status), exit_reason ? exit_reason : "none", time_s, crm_element_value(xml_op, XML_RSC_OP_T_QUEUE), crm_element_value(xml_op, XML_RSC_OP_T_EXEC)); crm_time_free(crm_when); free(time_s); return buf; } else { return crm_strdup_printf("%s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'", op_key ? op_key : ID(xml_op), crm_element_value(xml_op, XML_ATTR_UNAME), services_ocf_exitcode_str(rc), rc, crm_element_value(xml_op, XML_LRM_ATTR_CALLID), services_lrm_status_str(status), exit_reason ? exit_reason : "none"); } } static const char * get_cluster_stack(pe_working_set_t *data_set) { xmlNode *stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']", data_set->input, LOG_DEBUG); return stack? crm_element_value(stack, XML_NVPAIR_ATTR_VALUE) : "unknown"; } static char * last_changed_string(const char *last_written, const char *user, const char *client, const char *origin) { if (last_written != NULL || user != NULL || client != NULL || origin != NULL) { return crm_strdup_printf("%s%s%s%s%s%s%s", last_written ? last_written : "", user ? " by " : "", user ? user : "", client ? " via " : "", client ? client : "", origin ? " on " : "", origin ? origin : ""); } else { return strdup(""); } } static char * op_history_string(xmlNode *xml_op, const char *task, const char *interval_ms_s, int rc, gboolean print_timing) { const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); char *interval_str = NULL; char *buf = NULL; if (interval_ms_s && safe_str_neq(interval_ms_s, "0")) { char *pair = pcmk_format_nvpair("interval", interval_ms_s, "ms"); interval_str = crm_strdup_printf(" %s", pair); free(pair); } if (print_timing) { char *last_change_str = NULL; char *last_run_str = NULL; char *exec_str = NULL; char *queue_str = NULL; const char *value = NULL; time_t epoch = 0; if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, &epoch) == pcmk_ok) && (epoch > 0)) { char *time = pcmk_format_named_time(XML_RSC_OP_LAST_CHANGE, epoch); last_change_str = crm_strdup_printf(" %s", time); free(time); } if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_RUN, &epoch) == pcmk_ok) && (epoch > 0)) { char *time = pcmk_format_named_time(XML_RSC_OP_LAST_RUN, epoch); last_run_str = crm_strdup_printf(" %s", time); free(time); } value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC); if (value) { char *pair = pcmk_format_nvpair(XML_RSC_OP_T_EXEC, value, "ms"); exec_str = crm_strdup_printf(" %s", pair); free(pair); } value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE); if (value) { char *pair = pcmk_format_nvpair(XML_RSC_OP_T_QUEUE, value, "ms"); queue_str = crm_strdup_printf(" %s", pair); free(pair); } buf = crm_strdup_printf("(%s) %s:%s%s%s%s%s rc=%d (%s)", call, task, interval_str ? interval_str : "", last_change_str ? last_change_str : "", last_run_str ? last_run_str : "", exec_str ? exec_str : "", queue_str ? queue_str : "", rc, services_ocf_exitcode_str(rc)); if (last_change_str) { free(last_change_str); } if (last_run_str) { free(last_run_str); } if (exec_str) { free(exec_str); } if (queue_str) { free(queue_str); } } else { buf = crm_strdup_printf("(%s) %s%s%s", call, task, interval_str ? ":" : "", interval_str ? interval_str : ""); } if (interval_str) { free(interval_str); } return buf; } static char * resource_history_string(pe_resource_t *rsc, const char *rsc_id, gboolean all, int failcount, time_t last_failure) { char *buf = NULL; if (rsc == NULL) { buf = crm_strdup_printf("%s: orphan", rsc_id); } else if (all || failcount || last_failure > 0) { char *failcount_s = NULL; char *lastfail_s = NULL; if (failcount > 0) { failcount_s = crm_strdup_printf(" %s=%d", PCMK__FAIL_COUNT_PREFIX, failcount); } else { failcount_s = strdup(""); } if (last_failure > 0) { lastfail_s = crm_strdup_printf(" %s='%s'", PCMK__LAST_FAILURE_PREFIX, pcmk__epoch2str(&last_failure)); } buf = crm_strdup_printf("%s: migration-threshold=%d%s%s", rsc_id, rsc->migration_threshold, failcount_s, lastfail_s? lastfail_s : ""); free(failcount_s); free(lastfail_s); } else { buf = crm_strdup_printf("%s:", rsc_id); } return buf; } int pe__cluster_summary(pcmk__output_t *out, va_list args) { pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); gboolean print_clone_detail = va_arg(args, gboolean); gboolean show_stack = va_arg(args, gboolean); gboolean show_dc = va_arg(args, gboolean); gboolean show_times = va_arg(args, gboolean); gboolean show_counts = va_arg(args, gboolean); gboolean show_options = va_arg(args, gboolean); int rc = pcmk_rc_no_output; const char *stack_s = get_cluster_stack(data_set); if (show_stack) { SUMMARY_HEADER(rc, out); out->message(out, "cluster-stack", stack_s); } /* Always print DC if none, even if not requested */ if (data_set->dc_node == NULL || show_dc) { xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']", data_set->input, LOG_DEBUG); const char *dc_version_s = dc_version? crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE) : NULL; const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM); char *dc_name = data_set->dc_node ? pe__node_display_name(data_set->dc_node, print_clone_detail) : NULL; SUMMARY_HEADER(rc, out); out->message(out, "cluster-dc", data_set->dc_node, quorum, dc_version_s, dc_name); free(dc_name); } if (show_times) { const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN); const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER); const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT); const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG); SUMMARY_HEADER(rc, out); out->message(out, "cluster-times", last_written, user, client, origin); } if (show_counts) { SUMMARY_HEADER(rc, out); out->message(out, "cluster-counts", g_list_length(data_set->nodes), data_set->ninstances, data_set->disabled_resources, data_set->blocked_resources); } if (show_options) { SUMMARY_HEADER(rc, out); out->message(out, "cluster-options", data_set); } if (rc == pcmk_rc_ok) { out->end_list(out); } if (is_set(data_set->flags, pe_flag_maintenance_mode)) { out->message(out, "maint-mode"); rc = pcmk_rc_ok; } return rc; } int pe__cluster_summary_html(pcmk__output_t *out, va_list args) { pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); gboolean print_clone_detail = va_arg(args, gboolean); gboolean show_stack = va_arg(args, gboolean); gboolean show_dc = va_arg(args, gboolean); gboolean show_times = va_arg(args, gboolean); gboolean show_counts = va_arg(args, gboolean); gboolean show_options = va_arg(args, gboolean); int rc = pcmk_rc_no_output; const char *stack_s = get_cluster_stack(data_set); if (show_stack) { SUMMARY_HEADER(rc, out); out->message(out, "cluster-stack", stack_s); } /* Always print DC if none, even if not requested */ if (data_set->dc_node == NULL || show_dc) { xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']", data_set->input, LOG_DEBUG); const char *dc_version_s = dc_version? crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE) : NULL; const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM); char *dc_name = data_set->dc_node ? pe__node_display_name(data_set->dc_node, print_clone_detail) : NULL; SUMMARY_HEADER(rc, out); out->message(out, "cluster-dc", data_set->dc_node, quorum, dc_version_s, dc_name); free(dc_name); } if (show_times) { const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN); const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER); const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT); const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG); SUMMARY_HEADER(rc, out); out->message(out, "cluster-times", last_written, user, client, origin); } if (show_counts) { SUMMARY_HEADER(rc, out); out->message(out, "cluster-counts", g_list_length(data_set->nodes), data_set->ninstances, data_set->disabled_resources, data_set->blocked_resources); } if (show_options) { /* Kind of a hack - close the list we may have opened earlier in this * function so we can put all the options into their own list. We * only want to do this on HTML output, though. */ if (rc == pcmk_rc_ok) { out->end_list(out); } out->begin_list(out, NULL, NULL, "Config Options"); out->message(out, "cluster-options", data_set); } if (rc == pcmk_rc_ok) { out->end_list(out); } if (is_set(data_set->flags, pe_flag_maintenance_mode)) { out->message(out, "maint-mode"); rc = pcmk_rc_ok; } return rc; } char * pe__node_display_name(pe_node_t *node, bool print_detail) { char *node_name; const char *node_host = NULL; const char *node_id = NULL; int name_len; CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL)); /* Host is displayed only if this is a guest node */ if (pe__is_guest_node(node)) { pe_node_t *host_node = pe__current_node(node->details->remote_rsc); if (host_node && host_node->details) { node_host = host_node->details->uname; } if (node_host == NULL) { node_host = ""; /* so we at least get "uname@" to indicate guest */ } } /* Node ID is displayed if different from uname and detail is requested */ if (print_detail && safe_str_neq(node->details->uname, node->details->id)) { node_id = node->details->id; } /* Determine name length */ name_len = strlen(node->details->uname) + 1; if (node_host) { name_len += strlen(node_host) + 1; /* "@node_host" */ } if (node_id) { name_len += strlen(node_id) + 3; /* + " (node_id)" */ } /* Allocate and populate display name */ node_name = malloc(name_len); CRM_ASSERT(node_name != NULL); strcpy(node_name, node->details->uname); if (node_host) { strcat(node_name, "@"); strcat(node_name, node_host); } if (node_id) { strcat(node_name, " ("); strcat(node_name, node_id); strcat(node_name, ")"); } return node_name; } int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name , size_t pairs_count, ...) { xmlNodePtr xml_node = NULL; va_list args; CRM_ASSERT(tag_name != NULL); xml_node = pcmk__output_xml_peek_parent(out); CRM_ASSERT(xml_node != NULL); xml_node = is_list ? create_xml_node(xml_node, tag_name) : xmlNewChild(xml_node, NULL, (pcmkXmlStr) tag_name, NULL); va_start(args, pairs_count); while(pairs_count--) { const char *param_name = va_arg(args, const char *); const char *param_value = va_arg(args, const char *); if (param_name && param_value) { xmlSetProp(xml_node, (pcmkXmlStr)param_name, (pcmkXmlStr)param_value); } }; va_end(args); if (is_list) { pcmk__output_xml_push_parent(out, xml_node); } return pcmk_rc_ok; } int pe__ban_html(pcmk__output_t *out, va_list args) { pe_node_t *pe_node = va_arg(args, pe_node_t *); pe__location_t *location = va_arg(args, pe__location_t *); gboolean print_clone_detail = va_arg(args, gboolean); char *node_name = pe__node_display_name(pe_node, print_clone_detail); char *buf = crm_strdup_printf("%s\tprevents %s from running %son %s", location->id, location->rsc_lh->id, location->role_filter == RSC_ROLE_MASTER ? "as Master " : "", node_name); pcmk__output_create_html_node(out, "li", NULL, NULL, buf); free(node_name); free(buf); return pcmk_rc_ok; } int pe__ban_text(pcmk__output_t *out, va_list args) { pe_node_t *pe_node = va_arg(args, pe_node_t *); pe__location_t *location = va_arg(args, pe__location_t *); gboolean print_clone_detail = va_arg(args, gboolean); char *node_name = pe__node_display_name(pe_node, print_clone_detail); out->list_item(out, NULL, "%s\tprevents %s from running %son %s", location->id, location->rsc_lh->id, location->role_filter == RSC_ROLE_MASTER ? "as Master " : "", node_name); free(node_name); return pcmk_rc_ok; } int pe__ban_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "ban"); pe_node_t *pe_node = va_arg(args, pe_node_t *); pe__location_t *location = va_arg(args, pe__location_t *); gboolean print_clone_detail G_GNUC_UNUSED = va_arg(args, gboolean); char *weight_s = crm_itoa(pe_node->weight); xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) location->id); xmlSetProp(node, (pcmkXmlStr) "resource", (pcmkXmlStr) location->rsc_lh->id); xmlSetProp(node, (pcmkXmlStr) "node", (pcmkXmlStr) pe_node->details->uname); xmlSetProp(node, (pcmkXmlStr) "weight", (pcmkXmlStr) weight_s); xmlSetProp(node, (pcmkXmlStr) "master_only", (pcmkXmlStr) (location->role_filter == RSC_ROLE_MASTER ? "true" : "false")); free(weight_s); return pcmk_rc_ok; } int pe__cluster_counts_html(pcmk__output_t *out, va_list args) { xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li"); xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li"); unsigned int nnodes = va_arg(args, unsigned int); int nresources = va_arg(args, int); int ndisabled = va_arg(args, int); int nblocked = va_arg(args, int); char *nnodes_str = crm_strdup_printf("%d node%s configured", nnodes, pcmk__plural_s(nnodes)); pcmk_create_html_node(nodes_node, "span", NULL, NULL, nnodes_str); free(nnodes_str); if (ndisabled && nblocked) { char *s = crm_strdup_printf("%d resource instance%s configured (%d ", nresources, pcmk__plural_s(nresources), ndisabled); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED"); s = crm_strdup_printf(", %d ", nblocked); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED"); pcmk_create_html_node(resources_node, "span", NULL, NULL, " from further action due to failure)"); } else if (ndisabled && !nblocked) { char *s = crm_strdup_printf("%d resource instance%s configured (%d ", nresources, pcmk__plural_s(nresources), ndisabled); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED"); pcmk_create_html_node(resources_node, "span", NULL, NULL, ")"); } else if (!ndisabled && nblocked) { char *s = crm_strdup_printf("%d resource instance%s configured (%d ", nresources, pcmk__plural_s(nresources), nblocked); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED"); pcmk_create_html_node(resources_node, "span", NULL, NULL, " from further action due to failure)"); } else { char *s = crm_strdup_printf("%d resource instance%s configured", nresources, pcmk__plural_s(nresources)); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); } return pcmk_rc_ok; } int pe__cluster_counts_text(pcmk__output_t *out, va_list args) { unsigned int nnodes = va_arg(args, unsigned int); int nresources = va_arg(args, int); int ndisabled = va_arg(args, int); int nblocked = va_arg(args, int); out->list_item(out, NULL, "%d node%s configured", nnodes, pcmk__plural_s(nnodes)); if (ndisabled && nblocked) { out->list_item(out, NULL, "%d resource instance%s configured " "(%d DISABLED, %d BLOCKED from " "further action due to failure)", nresources, pcmk__plural_s(nresources), ndisabled, nblocked); } else if (ndisabled && !nblocked) { out->list_item(out, NULL, "%d resource instance%s configured " "(%d DISABLED)", nresources, pcmk__plural_s(nresources), ndisabled); } else if (!ndisabled && nblocked) { out->list_item(out, NULL, "%d resource instance%s configured " "(%d BLOCKED from further action " "due to failure)", nresources, pcmk__plural_s(nresources), nblocked); } else { out->list_item(out, NULL, "%d resource instance%s configured", nresources, pcmk__plural_s(nresources)); } return pcmk_rc_ok; } int pe__cluster_counts_xml(pcmk__output_t *out, va_list args) { xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "nodes_configured"); xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "resources_configured"); unsigned int nnodes = va_arg(args, unsigned int); int nresources = va_arg(args, int); int ndisabled = va_arg(args, int); int nblocked = va_arg(args, int); char *s = crm_itoa(nnodes); xmlSetProp(nodes_node, (pcmkXmlStr) "number", (pcmkXmlStr) s); free(s); s = crm_itoa(nresources); xmlSetProp(resources_node, (pcmkXmlStr) "number", (pcmkXmlStr) s); free(s); s = crm_itoa(ndisabled); xmlSetProp(resources_node, (pcmkXmlStr) "disabled", (pcmkXmlStr) s); free(s); s = crm_itoa(nblocked); xmlSetProp(resources_node, (pcmkXmlStr) "blocked", (pcmkXmlStr) s); free(s); return pcmk_rc_ok; } int pe__cluster_dc_html(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "li"); pe_node_t *dc = va_arg(args, pe_node_t *); const char *quorum = va_arg(args, const char *); const char *dc_version_s = va_arg(args, const char *); char *dc_name = va_arg(args, char *); pcmk_create_html_node(node, "span", NULL, "bold", "Current DC: "); if (dc) { if (crm_is_true(quorum)) { char *buf = crm_strdup_printf("%s (version %s) - partition with quorum", dc_name, dc_version_s ? dc_version_s : "unknown"); pcmk_create_html_node(node, "span", NULL, NULL, buf); free(buf); } else { char *buf = crm_strdup_printf("%s (version %s) - partition", dc_name, dc_version_s ? dc_version_s : "unknown"); pcmk_create_html_node(node, "span", NULL, NULL, buf); free(buf); pcmk_create_html_node(node, "span", NULL, "warning", "WITHOUT"); pcmk_create_html_node(node, "span", NULL, NULL, "quorum"); } } else { pcmk_create_html_node(node ,"span", NULL, "warning", "NONE"); } return pcmk_rc_ok; } int pe__cluster_dc_text(pcmk__output_t *out, va_list args) { pe_node_t *dc = va_arg(args, pe_node_t *); const char *quorum = va_arg(args, const char *); const char *dc_version_s = va_arg(args, const char *); char *dc_name = va_arg(args, char *); if (dc) { out->list_item(out, "Current DC", "%s (version %s) - partition %s quorum", dc_name, dc_version_s ? dc_version_s : "unknown", crm_is_true(quorum) ? "with" : "WITHOUT"); } else { out->list_item(out, "Current DC", "NONE"); } return pcmk_rc_ok; } int pe__cluster_dc_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "current_dc"); pe_node_t *dc = va_arg(args, pe_node_t *); const char *quorum = va_arg(args, const char *); const char *dc_version_s = va_arg(args, const char *); char *dc_name G_GNUC_UNUSED = va_arg(args, char *); if (dc) { xmlSetProp(node, (pcmkXmlStr) "present", (pcmkXmlStr) "true"); xmlSetProp(node, (pcmkXmlStr) "version", (pcmkXmlStr) (dc_version_s ? dc_version_s : "")); xmlSetProp(node, (pcmkXmlStr) "name", (pcmkXmlStr) dc->details->uname); xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) dc->details->id); xmlSetProp(node, (pcmkXmlStr) "with_quorum", (pcmkXmlStr) (crm_is_true(quorum) ? "true" : "false")); } else { xmlSetProp(node, (pcmkXmlStr) "present", (pcmkXmlStr) "false"); } return pcmk_rc_ok; } int pe__cluster_maint_mode_text(pcmk__output_t *out, va_list args) { fprintf(out->dest, "\n *** Resource management is DISABLED ***"); fprintf(out->dest, "\n The cluster will not attempt to start, stop or recover services"); fprintf(out->dest, "\n"); return pcmk_rc_ok; } int pe__cluster_options_html(pcmk__output_t *out, va_list args) { pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); out->list_item(out, NULL, "STONITH of failed nodes %s", is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled"); out->list_item(out, NULL, "Cluster is %s", is_set(data_set->flags, pe_flag_symmetric_cluster) ? "symmetric" : "asymmetric"); switch (data_set->no_quorum_policy) { case no_quorum_freeze: out->list_item(out, NULL, "No quorum policy: Freeze resources"); break; case no_quorum_stop: out->list_item(out, NULL, "No quorum policy: Stop ALL resources"); break; case no_quorum_ignore: out->list_item(out, NULL, "No quorum policy: Ignore"); break; case no_quorum_suicide: out->list_item(out, NULL, "No quorum policy: Suicide"); break; } if (is_set(data_set->flags, pe_flag_maintenance_mode)) { xmlNodePtr node = pcmk__output_create_xml_node(out, "li"); pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: "); pcmk_create_html_node(node, "span", NULL, "bold", "DISABLED"); pcmk_create_html_node(node, "span", NULL, NULL, " (the cluster will not attempt to start, stop, or recover services)"); } else { out->list_item(out, NULL, "Resource management: enabled"); } return pcmk_rc_ok; } int pe__cluster_options_log(pcmk__output_t *out, va_list args) { pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); if (is_set(data_set->flags, pe_flag_maintenance_mode)) { out->info(out, "Resource management is DISABLED. The cluster will not attempt to start, stop or recover services."); return pcmk_rc_ok; } else { return pcmk_rc_no_output; } } int pe__cluster_options_text(pcmk__output_t *out, va_list args) { pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); out->list_item(out, NULL, "STONITH of failed nodes %s", is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled"); out->list_item(out, NULL, "Cluster is %s", is_set(data_set->flags, pe_flag_symmetric_cluster) ? "symmetric" : "asymmetric"); switch (data_set->no_quorum_policy) { case no_quorum_freeze: out->list_item(out, NULL, "No quorum policy: Freeze resources"); break; case no_quorum_stop: out->list_item(out, NULL, "No quorum policy: Stop ALL resources"); break; case no_quorum_ignore: out->list_item(out, NULL, "No quorum policy: Ignore"); break; case no_quorum_suicide: out->list_item(out, NULL, "No quorum policy: Suicide"); break; } return pcmk_rc_ok; } int pe__cluster_options_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "cluster_options"); pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); xmlSetProp(node, (pcmkXmlStr) "stonith-enabled", (pcmkXmlStr) (is_set(data_set->flags, pe_flag_stonith_enabled) ? "true" : "false")); xmlSetProp(node, (pcmkXmlStr) "symmetric-cluster", (pcmkXmlStr) (is_set(data_set->flags, pe_flag_symmetric_cluster) ? "true" : "false")); switch (data_set->no_quorum_policy) { case no_quorum_freeze: xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "freeze"); break; case no_quorum_stop: xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "stop"); break; case no_quorum_ignore: xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "ignore"); break; case no_quorum_suicide: xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "suicide"); break; } xmlSetProp(node, (pcmkXmlStr) "maintenance-mode", (pcmkXmlStr) (is_set(data_set->flags, pe_flag_maintenance_mode) ? "true" : "false")); return pcmk_rc_ok; } int pe__cluster_stack_html(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "li"); const char *stack_s = va_arg(args, const char *); pcmk_create_html_node(node, "span", NULL, "bold", "Stack: "); pcmk_create_html_node(node, "span", NULL, NULL, stack_s); return pcmk_rc_ok; } int pe__cluster_stack_text(pcmk__output_t *out, va_list args) { const char *stack_s = va_arg(args, const char *); out->list_item(out, "Stack", "%s", stack_s); return pcmk_rc_ok; } int pe__cluster_stack_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "stack"); const char *stack_s = va_arg(args, const char *); xmlSetProp(node, (pcmkXmlStr) "type", (pcmkXmlStr) stack_s); return pcmk_rc_ok; } int pe__cluster_times_html(pcmk__output_t *out, va_list args) { xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li"); xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li"); const char *last_written = va_arg(args, const char *); const char *user = va_arg(args, const char *); const char *client = va_arg(args, const char *); const char *origin = va_arg(args, const char *); char *buf = last_changed_string(last_written, user, client, origin); pcmk_create_html_node(updated_node, "span", NULL, "bold", "Last updated: "); pcmk_create_html_node(updated_node, "span", NULL, NULL, pcmk__epoch2str(NULL)); pcmk_create_html_node(changed_node, "span", NULL, "bold", "Last change: "); pcmk_create_html_node(changed_node, "span", NULL, NULL, buf); free(buf); return pcmk_rc_ok; } int pe__cluster_times_xml(pcmk__output_t *out, va_list args) { xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "last_update"); xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "last_change"); const char *last_written = va_arg(args, const char *); const char *user = va_arg(args, const char *); const char *client = va_arg(args, const char *); const char *origin = va_arg(args, const char *); xmlSetProp(updated_node, (pcmkXmlStr) "time", (pcmkXmlStr) pcmk__epoch2str(NULL)); xmlSetProp(changed_node, (pcmkXmlStr) "time", (pcmkXmlStr) (last_written ? last_written : "")); xmlSetProp(changed_node, (pcmkXmlStr) "user", (pcmkXmlStr) (user ? user : "")); xmlSetProp(changed_node, (pcmkXmlStr) "client", (pcmkXmlStr) (client ? client : "")); xmlSetProp(changed_node, (pcmkXmlStr) "origin", (pcmkXmlStr) (origin ? origin : "")); return pcmk_rc_ok; } int pe__cluster_times_text(pcmk__output_t *out, va_list args) { const char *last_written = va_arg(args, const char *); const char *user = va_arg(args, const char *); const char *client = va_arg(args, const char *); const char *origin = va_arg(args, const char *); char *buf = last_changed_string(last_written, user, client, origin); out->list_item(out, "Last updated", "%s", pcmk__epoch2str(NULL)); out->list_item(out, "Last change", " %s", buf); free(buf); return pcmk_rc_ok; } int pe__failed_action_text(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); char *s = failed_action_string(xml_op); out->list_item(out, NULL, "%s", s); free(s); return pcmk_rc_ok; } int pe__failed_action_xml(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY); const char *last = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE); int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0"); int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0"); const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON); char *rc_s = crm_itoa(rc); char *reason_s = crm_xml_escape(exit_reason ? exit_reason : "none"); xmlNodePtr node = pcmk__output_create_xml_node(out, "failure"); xmlSetProp(node, (pcmkXmlStr) (op_key ? "op_key" : "id"), (pcmkXmlStr) (op_key ? op_key : "id")); xmlSetProp(node, (pcmkXmlStr) "node", (pcmkXmlStr) crm_element_value(xml_op, XML_ATTR_UNAME)); xmlSetProp(node, (pcmkXmlStr) "exitstatus", (pcmkXmlStr) services_ocf_exitcode_str(rc)); xmlSetProp(node, (pcmkXmlStr) "exitreason", (pcmkXmlStr) reason_s); xmlSetProp(node, (pcmkXmlStr) "exitcode", (pcmkXmlStr) rc_s); xmlSetProp(node, (pcmkXmlStr) "call", (pcmkXmlStr) crm_element_value(xml_op, XML_LRM_ATTR_CALLID)); xmlSetProp(node, (pcmkXmlStr) "status", (pcmkXmlStr) services_lrm_status_str(status)); if (last) { guint interval_ms = 0; char *s = NULL; time_t when = crm_parse_int(last, "0"); crm_time_t *crm_when = crm_time_new(NULL); char *rc_change = NULL; crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms); s = crm_itoa(interval_ms); crm_time_set_timet(crm_when, &when); rc_change = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone); xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_LAST_CHANGE, (pcmkXmlStr) rc_change); xmlSetProp(node, (pcmkXmlStr) "queued", (pcmkXmlStr) crm_element_value(xml_op, XML_RSC_OP_T_QUEUE)); xmlSetProp(node, (pcmkXmlStr) "exec", (pcmkXmlStr) crm_element_value(xml_op, XML_RSC_OP_T_EXEC)); xmlSetProp(node, (pcmkXmlStr) "interval", (pcmkXmlStr) s); xmlSetProp(node, (pcmkXmlStr) "task", (pcmkXmlStr) crm_element_value(xml_op, XML_LRM_ATTR_TASK)); free(s); free(rc_change); crm_time_free(crm_when); } free(reason_s); free(rc_s); return pcmk_rc_ok; } int pe__node_html(pcmk__output_t *out, va_list args) { pe_node_t *node = va_arg(args, pe_node_t *); unsigned int print_opts = va_arg(args, unsigned int); gboolean full = va_arg(args, gboolean); const char *node_mode G_GNUC_UNUSED = va_arg(args, const char *); gboolean print_clone_detail = va_arg(args, gboolean); gboolean print_brief = va_arg(args, gboolean); gboolean group_by_node = va_arg(args, gboolean); GListPtr only_show = va_arg(args, GListPtr); char *node_name = pe__node_display_name(node, print_clone_detail); char *buf = crm_strdup_printf("Node: %s", node_name); if (full) { xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li"); pcmk_create_html_node(item_node, "span", NULL, NULL, buf); if (node->details->standby_onfail && node->details->online) { pcmk_create_html_node(item_node, "span", NULL, "standby", " standby (on-fail)"); } else if (node->details->standby && node->details->online) { char *s = crm_strdup_printf(" standby%s", node->details->running_rsc ? " (with active resources)" : ""); pcmk_create_html_node(item_node, "span", NULL, " standby", s); free(s); } else if (node->details->standby) { pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE (standby)"); } else if (node->details->maintenance && node->details->online) { pcmk_create_html_node(item_node, "span", NULL, "maint", " maintenance"); } else if (node->details->maintenance) { pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE (maintenance)"); } else if (node->details->online) { pcmk_create_html_node(item_node, "span", NULL, "online", " online"); } else { pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE"); } if (print_brief && group_by_node) { out->begin_list(out, NULL, NULL, NULL); pe__rscs_brief_output(out, node->details->running_rsc, print_opts | pe_print_rsconly, FALSE); out->end_list(out); } else if (group_by_node) { GListPtr lpc2 = NULL; out->begin_list(out, NULL, NULL, NULL); for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) { pe_resource_t *rsc = (pe_resource_t *) lpc2->data; out->message(out, crm_map_element_name(rsc->xml), print_opts | pe_print_rsconly, rsc, only_show); } out->end_list(out); } } else { out->begin_list(out, NULL, NULL, "%s", buf); } free(buf); free(node_name); return pcmk_rc_ok; } int pe__node_text(pcmk__output_t *out, va_list args) { pe_node_t *node = va_arg(args, pe_node_t *); unsigned int print_opts = va_arg(args, unsigned int); gboolean full = va_arg(args, gboolean); const char *node_mode = va_arg(args, const char *); gboolean print_clone_detail = va_arg(args, gboolean); gboolean print_brief = va_arg(args, gboolean); gboolean group_by_node = va_arg(args, gboolean); GListPtr only_show = va_arg(args, GListPtr); if (full) { char *node_name = pe__node_display_name(node, print_clone_detail); char *buf = NULL; /* Print the node name and status */ if (pe__is_guest_node(node)) { buf = crm_strdup_printf("GuestNode %s: %s", node_name, node_mode); } else if (pe__is_remote_node(node)) { buf = crm_strdup_printf("RemoteNode %s: %s", node_name, node_mode); } else { buf = crm_strdup_printf("Node %s: %s", node_name, node_mode); } /* If we're grouping by node, print its resources */ if (group_by_node) { out->begin_list(out, NULL, NULL, "%s", buf); out->begin_list(out, NULL, NULL, "Resources"); if (print_brief) { pe__rscs_brief_output(out, node->details->running_rsc, print_opts | pe_print_rsconly, FALSE); } else { GListPtr gIter2 = NULL; for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) { pe_resource_t *rsc = (pe_resource_t *) gIter2->data; out->message(out, crm_map_element_name(rsc->xml), print_opts | pe_print_rsconly, rsc, only_show); } } out->end_list(out); out->end_list(out); } else { out->list_item(out, NULL, "%s", buf); } free(buf); free(node_name); } else { out->begin_list(out, NULL, NULL, "Node: %s", pe__node_display_name(node, print_clone_detail)); } return pcmk_rc_ok; } int pe__node_xml(pcmk__output_t *out, va_list args) { pe_node_t *node = va_arg(args, pe_node_t *); unsigned int print_opts = va_arg(args, unsigned int); gboolean full = va_arg(args, gboolean); const char *node_mode G_GNUC_UNUSED = va_arg(args, const char *); gboolean print_clone_detail G_GNUC_UNUSED = va_arg(args, gboolean); gboolean print_brief G_GNUC_UNUSED = va_arg(args, gboolean); gboolean group_by_node = va_arg(args, gboolean); GListPtr only_show = va_arg(args, GListPtr); if (full) { const char *node_type = "unknown"; char *length_s = crm_itoa(g_list_length(node->details->running_rsc)); switch (node->details->type) { case node_member: node_type = "member"; break; case node_remote: node_type = "remote"; break; case node_ping: node_type = "ping"; break; } pe__name_and_nvpairs_xml(out, true, "node", 13, "name", node->details->uname, "id", node->details->id, "online", node->details->online ? "true" : "false", "standby", node->details->standby ? "true" : "false", "standby_onfail", node->details->standby_onfail ? "true" : "false", "maintenance", node->details->maintenance ? "true" : "false", "pending", node->details->pending ? "true" : "false", "unclean", node->details->unclean ? "true" : "false", "shutdown", node->details->shutdown ? "true" : "false", "expected_up", node->details->expected_up ? "true" : "false", "is_dc", node->details->is_dc ? "true" : "false", "resources_running", length_s, "type", node_type); if (pe__is_guest_node(node)) { xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out); xmlSetProp(xml_node, (pcmkXmlStr) "id_as_resource", (pcmkXmlStr) node->details->remote_rsc->container->id); } if (group_by_node) { GListPtr lpc = NULL; for (lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) { pe_resource_t *rsc = (pe_resource_t *) lpc->data; out->message(out, crm_map_element_name(rsc->xml), print_opts | pe_print_rsconly, rsc, only_show); } } free(length_s); out->end_list(out); } else { xmlNodePtr parent = pcmk__output_xml_create_parent(out, "node"); xmlSetProp(parent, (pcmkXmlStr) "name", (pcmkXmlStr) node->details->uname); } return pcmk_rc_ok; } int pe__node_attribute_text(pcmk__output_t *out, va_list args) { const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); gboolean add_extra = va_arg(args, gboolean); int expected_score = va_arg(args, int); if (add_extra) { int v = crm_parse_int(value, "0"); if (v <= 0) { out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is lost", name, value); } else if (v < expected_score) { out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is degraded (Expected=%d)", name, value, expected_score); } else { out->list_item(out, NULL, "%-32s\t: %-10s", name, value); } } else { out->list_item(out, NULL, "%-32s\t: %-10s", name, value); } return pcmk_rc_ok; } int pe__node_attribute_html(pcmk__output_t *out, va_list args) { const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); gboolean add_extra = va_arg(args, gboolean); int expected_score = va_arg(args, int); if (add_extra) { int v = crm_parse_int(value, "0"); char *s = crm_strdup_printf("%s: %s", name, value); xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li"); pcmk_create_html_node(item_node, "span", NULL, NULL, s); free(s); if (v <= 0) { pcmk_create_html_node(item_node, "span", NULL, "bold", "(connectivity is lost)"); } else if (v < expected_score) { char *buf = crm_strdup_printf("(connectivity is degraded -- expected %d", expected_score); pcmk_create_html_node(item_node, "span", NULL, "bold", buf); free(buf); } } else { out->list_item(out, NULL, "%s: %s", name, value); } return pcmk_rc_ok; } int pe__node_attribute_xml(pcmk__output_t *out, va_list args) { const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); gboolean add_extra = va_arg(args, gboolean); int expected_score = va_arg(args, int); xmlNodePtr node = pcmk__output_create_xml_node(out, "attribute"); xmlSetProp(node, (pcmkXmlStr) "name", (pcmkXmlStr) name); xmlSetProp(node, (pcmkXmlStr) "value", (pcmkXmlStr) value); if (add_extra) { char *buf = crm_itoa(expected_score); xmlSetProp(node, (pcmkXmlStr) "expected", (pcmkXmlStr) buf); free(buf); } return pcmk_rc_ok; } +int +pe__node_list_html(pcmk__output_t *out, va_list args) { + GListPtr nodes = va_arg(args, GListPtr); + GListPtr only_show = va_arg(args, GListPtr); + unsigned int print_opts = va_arg(args, unsigned int); + gboolean print_clone_detail = va_arg(args, gboolean); + gboolean print_brief = va_arg(args, gboolean); + gboolean group_by_node = va_arg(args, gboolean); + + gboolean printed_header = FALSE; + + for (GListPtr gIter = nodes; gIter != NULL; gIter = gIter->next) { + pe_node_t *node = (pe_node_t *) gIter->data; + + if (!pcmk__str_in_list(only_show, node->details->uname)) { + continue; + } + + if (printed_header == FALSE) { + printed_header = TRUE; + out->begin_list(out, NULL, NULL, "Node List"); + } + + out->message(out, "node", node, print_opts, TRUE, NULL, print_clone_detail, + print_brief, group_by_node, only_show); + } + + if (printed_header == TRUE) { + out->end_list(out); + } + + return pcmk_rc_ok; +} + +int +pe__node_list_text(pcmk__output_t *out, va_list args) { + GListPtr nodes = va_arg(args, GListPtr); + GListPtr only_show = va_arg(args, GListPtr); + unsigned int print_opts = va_arg(args, unsigned int); + gboolean print_clone_detail = va_arg(args, gboolean); + gboolean print_brief = va_arg(args, gboolean); + gboolean group_by_node = va_arg(args, gboolean); + + /* space-separated lists of node names */ + char *online_nodes = NULL; + char *online_remote_nodes = NULL; + char *online_guest_nodes = NULL; + char *offline_nodes = NULL; + char *offline_remote_nodes = NULL; + + int rc = pcmk_rc_no_output; + gboolean printed_header = FALSE; + + for (GListPtr gIter = nodes; gIter != NULL; gIter = gIter->next) { + pe_node_t *node = (pe_node_t *) gIter->data; + const char *node_mode = NULL; + char *node_name = pe__node_display_name(node, print_clone_detail); + + if (!pcmk__str_in_list(only_show, node->details->uname)) { + free(node_name); + continue; + } + + if (printed_header == FALSE) { + out->begin_list(out, NULL, NULL, "Node List"); + rc = pcmk_rc_ok; + printed_header = TRUE; + } + + /* Get node mode */ + if (node->details->unclean) { + if (node->details->online) { + node_mode = "UNCLEAN (online)"; + + } else if (node->details->pending) { + node_mode = "UNCLEAN (pending)"; + + } else { + node_mode = "UNCLEAN (offline)"; + } + + } else if (node->details->pending) { + node_mode = "pending"; + + } else if (node->details->standby_onfail && node->details->online) { + node_mode = "standby (on-fail)"; + + } else if (node->details->standby) { + if (node->details->online) { + if (node->details->running_rsc) { + node_mode = "standby (with active resources)"; + } else { + node_mode = "standby"; + } + } else { + node_mode = "OFFLINE (standby)"; + } + + } else if (node->details->maintenance) { + if (node->details->online) { + node_mode = "maintenance"; + } else { + node_mode = "OFFLINE (maintenance)"; + } + + } else if (node->details->online) { + node_mode = "online"; + if (group_by_node == FALSE) { + if (pe__is_guest_node(node)) { + online_guest_nodes = pcmk__add_word(online_guest_nodes, + node_name); + } else if (pe__is_remote_node(node)) { + online_remote_nodes = pcmk__add_word(online_remote_nodes, + node_name); + } else { + online_nodes = pcmk__add_word(online_nodes, node_name); + } + free(node_name); + continue; + } + + } else { + node_mode = "OFFLINE"; + if (group_by_node == FALSE) { + if (pe__is_remote_node(node)) { + offline_remote_nodes = pcmk__add_word(offline_remote_nodes, + node_name); + } else if (pe__is_guest_node(node)) { + /* ignore offline guest nodes */ + } else { + offline_nodes = pcmk__add_word(offline_nodes, node_name); + } + free(node_name); + continue; + } + } + + /* If we get here, node is in bad state, or we're grouping by node */ + out->message(out, "node", node, print_opts, TRUE, node_mode, print_clone_detail, + print_brief, group_by_node, only_show); + free(node_name); + } + + /* If we're not grouping by node, summarize nodes by status */ + if (online_nodes) { + out->list_item(out, "Online", "[%s ]", online_nodes); + free(online_nodes); + } + if (offline_nodes) { + out->list_item(out, "OFFLINE", "[%s ]", offline_nodes); + free(offline_nodes); + } + if (online_remote_nodes) { + out->list_item(out, "RemoteOnline", "[%s ]", online_remote_nodes); + free(online_remote_nodes); + } + if (offline_remote_nodes) { + out->list_item(out, "RemoteOFFLINE", "[%s ]", offline_remote_nodes); + free(offline_remote_nodes); + } + if (online_guest_nodes) { + out->list_item(out, "GuestOnline", "[%s ]", online_guest_nodes); + free(online_guest_nodes); + } + + if (printed_header == TRUE) { + out->end_list(out); + } + + return rc; +} + +int +pe__node_list_xml(pcmk__output_t *out, va_list args) { + GListPtr nodes = va_arg(args, GListPtr); + GListPtr only_show = va_arg(args, GListPtr); + unsigned int print_opts = va_arg(args, unsigned int); + gboolean print_clone_detail = va_arg(args, gboolean); + gboolean print_brief = va_arg(args, gboolean); + gboolean group_by_node = va_arg(args, gboolean); + + out->begin_list(out, NULL, NULL, "nodes"); + for (GListPtr gIter = nodes; gIter != NULL; gIter = gIter->next) { + pe_node_t *node = (pe_node_t *) gIter->data; + + if (!pcmk__str_in_list(only_show, node->details->uname)) { + continue; + } + + out->message(out, "node", node, print_opts, TRUE, NULL, print_clone_detail, + print_brief, group_by_node, only_show); + } + out->end_list(out); + + return pcmk_rc_ok; +} + int pe__op_history_text(pcmk__output_t *out, va_list args) { xmlNode *xml_op = va_arg(args, xmlNode *); const char *task = va_arg(args, const char *); const char *interval_ms_s = va_arg(args, const char *); int rc = va_arg(args, int); gboolean print_timing = va_arg(args, gboolean); char *buf = op_history_string(xml_op, task, interval_ms_s, rc, print_timing); out->list_item(out, NULL, "%s", buf); free(buf); return pcmk_rc_ok; } int pe__op_history_xml(pcmk__output_t *out, va_list args) { xmlNode *xml_op = va_arg(args, xmlNode *); const char *task = va_arg(args, const char *); const char *interval_ms_s = va_arg(args, const char *); int rc = va_arg(args, int); gboolean print_timing = va_arg(args, gboolean); char *rc_s = NULL; xmlNodePtr node = pcmk__output_create_xml_node(out, "operation_history"); xmlSetProp(node, (pcmkXmlStr) "call", (pcmkXmlStr) crm_element_value(xml_op, XML_LRM_ATTR_CALLID)); xmlSetProp(node, (pcmkXmlStr) "task", (pcmkXmlStr) task); if (interval_ms_s && safe_str_neq(interval_ms_s, "0")) { char *s = crm_strdup_printf("%sms", interval_ms_s); xmlSetProp(node, (pcmkXmlStr) "interval", (pcmkXmlStr) s); free(s); } if (print_timing) { const char *value = NULL; value = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE); if (value) { time_t int_value = (time_t) crm_parse_int(value, NULL); if (int_value > 0) { xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_LAST_CHANGE, (pcmkXmlStr) pcmk__epoch2str(&int_value)); } } value = crm_element_value(xml_op, XML_RSC_OP_LAST_RUN); if (value) { time_t int_value = (time_t) crm_parse_int(value, NULL); if (int_value > 0) { xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_LAST_RUN, (pcmkXmlStr) pcmk__epoch2str(&int_value)); } } value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC); if (value) { char *s = crm_strdup_printf("%sms", value); xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_T_EXEC, (pcmkXmlStr) s); free(s); } value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE); if (value) { char *s = crm_strdup_printf("%sms", value); xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_T_QUEUE, (pcmkXmlStr) s); free(s); } } rc_s = crm_itoa(rc); xmlSetProp(node, (pcmkXmlStr) "rc", (pcmkXmlStr) rc_s); xmlSetProp(node, (pcmkXmlStr) "rc_text", (pcmkXmlStr) services_ocf_exitcode_str(rc)); free(rc_s); return pcmk_rc_ok; } int pe__resource_history_text(pcmk__output_t *out, va_list args) { pe_resource_t *rsc = va_arg(args, pe_resource_t *); const char *rsc_id = va_arg(args, const char *); gboolean all = va_arg(args, gboolean); int failcount = va_arg(args, int); time_t last_failure = va_arg(args, int); gboolean as_header = va_arg(args, gboolean); char *buf = resource_history_string(rsc, rsc_id, all, failcount, last_failure); if (as_header) { out->begin_list(out, NULL, NULL, "%s", buf); } else { out->list_item(out, NULL, "%s", buf); } free(buf); return pcmk_rc_ok; } int pe__resource_history_xml(pcmk__output_t *out, va_list args) { pe_resource_t *rsc = va_arg(args, pe_resource_t *); const char *rsc_id = va_arg(args, const char *); gboolean all = va_arg(args, gboolean); int failcount = va_arg(args, int); time_t last_failure = va_arg(args, int); gboolean as_header = va_arg(args, gboolean); xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource_history"); xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) rsc_id); if (rsc == NULL) { xmlSetProp(node, (pcmkXmlStr) "orphan", (pcmkXmlStr) "true"); } else if (all || failcount || last_failure > 0) { char *migration_s = crm_itoa(rsc->migration_threshold); xmlSetProp(node, (pcmkXmlStr) "orphan", (pcmkXmlStr) "false"); xmlSetProp(node, (pcmkXmlStr) "migration-threshold", (pcmkXmlStr) migration_s); free(migration_s); if (failcount > 0) { char *s = crm_itoa(failcount); xmlSetProp(node, (pcmkXmlStr) PCMK__FAIL_COUNT_PREFIX, (pcmkXmlStr) s); free(s); } if (last_failure > 0) { xmlSetProp(node, (pcmkXmlStr) PCMK__LAST_FAILURE_PREFIX, (pcmkXmlStr) pcmk__epoch2str(&last_failure)); } } if (as_header == FALSE) { pcmk__output_xml_pop_parent(out); } return pcmk_rc_ok; } int pe__ticket_html(pcmk__output_t *out, va_list args) { pe_ticket_t *ticket = va_arg(args, pe_ticket_t *); if (ticket->last_granted > -1) { char *time = pcmk_format_named_time("last-granted", ticket->last_granted); out->list_item(out, NULL, "%s:\t%s%s %s", ticket->id, ticket->granted ? "granted" : "revoked", ticket->standby ? " [standby]" : "", time); free(time); } else { out->list_item(out, NULL, "%s:\t%s%s", ticket->id, ticket->granted ? "granted" : "revoked", ticket->standby ? " [standby]" : ""); } return pcmk_rc_ok; } int pe__ticket_text(pcmk__output_t *out, va_list args) { pe_ticket_t *ticket = va_arg(args, pe_ticket_t *); if (ticket->last_granted > -1) { char *time = pcmk_format_named_time("last-granted", ticket->last_granted); out->list_item(out, ticket->id, "\t%s%s %s", ticket->granted ? "granted" : "revoked", ticket->standby ? " [standby]" : "", time); free(time); } else { out->list_item(out, ticket->id, "\t%s%s", ticket->granted ? "granted" : "revoked", ticket->standby ? " [standby]" : ""); } return pcmk_rc_ok; } int pe__ticket_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = NULL; pe_ticket_t *ticket = va_arg(args, pe_ticket_t *); node = pcmk__output_create_xml_node(out, "ticket"); xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) ticket->id); xmlSetProp(node, (pcmkXmlStr) "status", (pcmkXmlStr) (ticket->granted ? "granted" : "revoked")); xmlSetProp(node, (pcmkXmlStr) "standby", (pcmkXmlStr) (ticket->standby ? "true" : "false")); if (ticket->last_granted > -1) { xmlSetProp(node, (pcmkXmlStr) "last-granted", (pcmkXmlStr) pcmk__epoch2str(&ticket->last_granted)); } return pcmk_rc_ok; } static pcmk__message_entry_t fmt_functions[] = { { "ban", "html", pe__ban_html }, { "ban", "log", pe__ban_text }, { "ban", "text", pe__ban_text }, { "ban", "xml", pe__ban_xml }, { "bundle", "xml", pe__bundle_xml }, { "bundle", "html", pe__bundle_html }, { "bundle", "text", pe__bundle_text }, { "bundle", "log", pe__bundle_text }, { "clone", "xml", pe__clone_xml }, { "clone", "html", pe__clone_html }, { "clone", "text", pe__clone_text }, { "clone", "log", pe__clone_text }, { "cluster-counts", "html", pe__cluster_counts_html }, { "cluster-counts", "log", pe__cluster_counts_text }, { "cluster-counts", "text", pe__cluster_counts_text }, { "cluster-counts", "xml", pe__cluster_counts_xml }, { "cluster-dc", "html", pe__cluster_dc_html }, { "cluster-dc", "log", pe__cluster_dc_text }, { "cluster-dc", "text", pe__cluster_dc_text }, { "cluster-dc", "xml", pe__cluster_dc_xml }, { "cluster-options", "html", pe__cluster_options_html }, { "cluster-options", "log", pe__cluster_options_log }, { "cluster-options", "text", pe__cluster_options_text }, { "cluster-options", "xml", pe__cluster_options_xml }, { "cluster-summary", "html", pe__cluster_summary_html }, { "cluster-summary", "log", pe__cluster_summary }, { "cluster-summary", "text", pe__cluster_summary }, { "cluster-summary", "xml", pe__cluster_summary }, { "cluster-stack", "html", pe__cluster_stack_html }, { "cluster-stack", "log", pe__cluster_stack_text }, { "cluster-stack", "text", pe__cluster_stack_text }, { "cluster-stack", "xml", pe__cluster_stack_xml }, { "cluster-times", "html", pe__cluster_times_html }, { "cluster-times", "log", pe__cluster_times_text }, { "cluster-times", "text", pe__cluster_times_text }, { "cluster-times", "xml", pe__cluster_times_xml }, { "failed-action", "html", pe__failed_action_text }, { "failed-action", "log", pe__failed_action_text }, { "failed-action", "text", pe__failed_action_text }, { "failed-action", "xml", pe__failed_action_xml }, { "group", "xml", pe__group_xml }, { "group", "html", pe__group_html }, { "group", "text", pe__group_text }, { "group", "log", pe__group_text }, /* maint-mode only exists for text and log. Other formatters output it as * part of the cluster-options handler. */ { "maint-mode", "log", pe__cluster_maint_mode_text }, { "maint-mode", "text", pe__cluster_maint_mode_text }, { "node", "html", pe__node_html }, { "node", "log", pe__node_text }, { "node", "text", pe__node_text }, { "node", "xml", pe__node_xml }, + { "node-list", "html", pe__node_list_html }, + { "node-list", "log", pe__node_list_text }, + { "node-list", "text", pe__node_list_text }, + { "node-list", "xml", pe__node_list_xml }, { "node-attribute", "html", pe__node_attribute_html }, { "node-attribute", "log", pe__node_attribute_text }, { "node-attribute", "text", pe__node_attribute_text }, { "node-attribute", "xml", pe__node_attribute_xml }, { "op-history", "html", pe__op_history_text }, { "op-history", "log", pe__op_history_text }, { "op-history", "text", pe__op_history_text }, { "op-history", "xml", pe__op_history_xml }, { "primitive", "xml", pe__resource_xml }, { "primitive", "html", pe__resource_html }, { "primitive", "text", pe__resource_text }, { "primitive", "log", pe__resource_text }, { "resource-history", "html", pe__resource_history_text }, { "resource-history", "log", pe__resource_history_text }, { "resource-history", "text", pe__resource_history_text }, { "resource-history", "xml", pe__resource_history_xml }, { "ticket", "html", pe__ticket_html }, { "ticket", "log", pe__ticket_text }, { "ticket", "text", pe__ticket_text }, { "ticket", "xml", pe__ticket_xml }, { NULL, NULL, NULL } }; void pe__register_messages(pcmk__output_t *out) { pcmk__register_messages(out, fmt_functions); } void pe__output_node(pe_node_t *node, gboolean details, pcmk__output_t *out) { if (node == NULL) { crm_trace(""); return; } CRM_ASSERT(node->details); crm_trace("%sNode %s: (weight=%d, fixed=%s)", node->details->online ? "" : "Unavailable/Unclean ", node->details->uname, node->weight, node->fixed ? "True" : "False"); if (details) { char *pe_mutable = strdup("\t\t"); GListPtr gIter = node->details->running_rsc; GListPtr unames = NULL; unames = g_list_prepend(unames, strdup("*")); crm_trace("\t\t===Node Attributes"); g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable); free(pe_mutable); crm_trace("\t\t=== Resources"); for (; gIter != NULL; gIter = gIter->next) { pe_resource_t *rsc = (pe_resource_t *) gIter->data; out->message(out, crm_map_element_name(rsc->xml), pe_print_pending, rsc, unames); } g_list_free_full(unames, free); } } diff --git a/tools/crm_mon_print.c b/tools/crm_mon_print.c index 8c1a80008a..ba5207de9d 100644 --- a/tools/crm_mon_print.c +++ b/tools/crm_mon_print.c @@ -1,1244 +1,1095 @@ /* * Copyright 2019-2020 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "crm_mon.h" static void print_resources_heading(pcmk__output_t *out, unsigned int mon_ops); static void print_resources_closing(pcmk__output_t *out, unsigned int mon_ops); static int print_resources(pcmk__output_t *out, pe_working_set_t *data_set, unsigned int print_opts, unsigned int mon_ops, gboolean brief_output, gboolean print_summary, GListPtr only_show, gboolean print_spacer); static int print_rsc_history(pcmk__output_t *out, pe_working_set_t *data_set, pe_node_t *node, xmlNode *rsc_entry, unsigned int mon_ops, GListPtr op_list); static int print_node_history(pcmk__output_t *out, pe_working_set_t *data_set, pe_node_t *node, xmlNode *node_state, gboolean operations, unsigned int mon_ops, GListPtr only_show); static gboolean add_extra_info(pcmk__output_t *out, pe_node_t * node, GListPtr rsc_list, const char *attrname, int *expected_score); static void print_node_attribute(gpointer name, gpointer user_data); static int print_node_summary(pcmk__output_t *out, pe_working_set_t * data_set, gboolean operations, unsigned int mon_ops, GListPtr only_snow, gboolean print_spacer); static int print_cluster_tickets(pcmk__output_t *out, pe_working_set_t * data_set, gboolean print_spacer); static int print_neg_locations(pcmk__output_t *out, pe_working_set_t *data_set, unsigned int mon_ops, const char *prefix, gboolean print_spacer); static int print_node_attributes(pcmk__output_t *out, pe_working_set_t *data_set, unsigned int mon_ops, GListPtr only_show, gboolean print_spacer); static int print_failed_actions(pcmk__output_t *out, pe_working_set_t *data_set, GListPtr only_show, gboolean print_spacer); static GListPtr build_uname_list(pe_working_set_t *data_set, const char *s) { GListPtr unames = NULL; if (s == NULL || strcmp(s, "*") == 0) { /* Nothing was given so return a list of all node names. Or, '*' was * given. This would normally fall into the pe__unames_with_tag branch * where it will return an empty list. Catch it here instead. */ unames = g_list_prepend(unames, strdup("*")); } else { pe_node_t *node = pe_find_node(data_set->nodes, s); if (node) { /* The given string was a valid uname for a node. Return a * singleton list containing just that uname. */ unames = g_list_prepend(unames, strdup(s)); } else { /* The given string was not a valid uname. It's either a tag or * it's a typo or something. In the first case, we'll return a * list of all the unames of the nodes with the given tag. In the * second case, we'll return a NULL pointer and nothing will * get displayed. */ unames = pe__unames_with_tag(data_set, s); } } return unames; } /*! * \internal * \brief Print resources section heading appropriate to options * * \param[in] out The output functions structure. * \param[in] mon_ops Bitmask of mon_op_*. */ static void print_resources_heading(pcmk__output_t *out, unsigned int mon_ops) { const char *heading; if (is_set(mon_ops, mon_op_group_by_node)) { /* Active resources have already been printed by node */ heading = is_set(mon_ops, mon_op_inactive_resources) ? "Inactive Resources" : NULL; } else if (is_set(mon_ops, mon_op_inactive_resources)) { heading = "Full List of Resources"; } else { heading = "Active Resources"; } /* Print section heading */ out->begin_list(out, NULL, NULL, "%s", heading); } /*! * \internal * \brief Print whatever resource section closing is appropriate * * \param[in] out The output functions structure. * \param[in] mon_ops Bitmask of mon_op_*. */ static void print_resources_closing(pcmk__output_t *out, unsigned int mon_ops) { const char *heading; /* What type of resources we did or did not display */ if (is_set(mon_ops, mon_op_group_by_node)) { heading = "inactive "; } else if (is_set(mon_ops, mon_op_inactive_resources)) { heading = ""; } else { heading = "active "; } out->list_item(out, NULL, "No %sresources", heading); } /*! * \internal * \brief Print whatever resource section(s) are appropriate * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] print_opts Bitmask of pe_print_*. * \param[in] mon_ops Bitmask of mon_op_*. * \param[in] brief_output Whether to display full or brief output. * \param[in] print_summary Whether to display a failure summary. */ static int print_resources(pcmk__output_t *out, pe_working_set_t *data_set, unsigned int print_opts, unsigned int mon_ops, gboolean brief_output, gboolean print_summary, GListPtr only_show, gboolean print_spacer) { GListPtr rsc_iter; int rc = pcmk_rc_no_output; /* If we already showed active resources by node, and * we're not showing inactive resources, we have nothing to do */ if (is_set(mon_ops, mon_op_group_by_node) && is_not_set(mon_ops, mon_op_inactive_resources)) { return rc; } /* Add a blank line between this section and the one before it. */ if (print_spacer) { out->info(out, "%s", ""); } print_resources_heading(out, mon_ops); /* If we haven't already printed resources grouped by node, * and brief output was requested, print resource summary */ if (brief_output && is_not_set(mon_ops, mon_op_group_by_node)) { pe__rscs_brief_output(out, data_set->resources, print_opts, is_set(mon_ops, mon_op_inactive_resources)); } /* For each resource, display it if appropriate */ for (rsc_iter = data_set->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) { pe_resource_t *rsc = (pe_resource_t *) rsc_iter->data; /* Complex resources may have some sub-resources active and some inactive */ gboolean is_active = rsc->fns->active(rsc, TRUE); gboolean partially_active = rsc->fns->active(rsc, FALSE); /* Skip inactive orphans (deleted but still in CIB) */ if (is_set(rsc->flags, pe_rsc_orphan) && !is_active) { continue; /* Skip active resources if we already displayed them by node */ } else if (is_set(mon_ops, mon_op_group_by_node)) { if (is_active) { continue; } /* Skip primitives already counted in a brief summary */ } else if (brief_output && (rsc->variant == pe_native)) { continue; /* Skip resources that aren't at least partially active, * unless we're displaying inactive resources */ } else if (!partially_active && is_not_set(mon_ops, mon_op_inactive_resources)) { continue; } if (is_active || partially_active) { if (!pe__rsc_running_on_any_node_in_list(rsc->running_on, only_show)) { continue; } } /* Print this resource */ rc = pcmk_rc_ok; out->message(out, crm_map_element_name(rsc->xml), print_opts, rsc, only_show); } if (print_summary && rc != pcmk_rc_ok) { print_resources_closing(out, mon_ops); } out->end_list(out); return pcmk_rc_ok; } static int failure_count(pe_working_set_t *data_set, pe_node_t *node, pe_resource_t *rsc, time_t *last_failure) { return rsc ? pe_get_failcount(node, rsc, last_failure, pe_fc_default, NULL, data_set) : 0; } static GListPtr get_operation_list(xmlNode *rsc_entry) { GListPtr op_list = NULL; xmlNode *rsc_op = NULL; for (rsc_op = __xml_first_child_element(rsc_entry); rsc_op != NULL; rsc_op = __xml_next_element(rsc_op)) { if (crm_str_eq((const char *) rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) { op_list = g_list_append(op_list, rsc_op); } } op_list = g_list_sort(op_list, sort_op_by_callid); return op_list; } /*! * \internal * \brief Print resource operation/failure history * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] node Node that ran this resource. * \param[in] rsc_entry Root of XML tree describing resource status. * \param[in] mon_ops Bitmask of mon_op_*. * \param[in] op_list A list of operations to print. */ static int print_rsc_history(pcmk__output_t *out, pe_working_set_t *data_set, pe_node_t *node, xmlNode *rsc_entry, unsigned int mon_ops, GListPtr op_list) { GListPtr gIter = NULL; int rc = pcmk_rc_no_output; const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); pe_resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); /* Print each operation */ for (gIter = op_list; gIter != NULL; gIter = gIter->next) { xmlNode *xml_op = (xmlNode *) gIter->data; const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *interval_ms_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS); const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); int op_rc_i = crm_parse_int(op_rc, "0"); /* Display 0-interval monitors as "probe" */ if (safe_str_eq(task, CRMD_ACTION_STATUS) && ((interval_ms_s == NULL) || safe_str_eq(interval_ms_s, "0"))) { task = "probe"; } /* Ignore notifies and some probes */ if (safe_str_eq(task, CRMD_ACTION_NOTIFY) || (safe_str_eq(task, "probe") && (op_rc_i == 7))) { continue; } /* If this is the first printed operation, print heading for resource */ if (rc == pcmk_rc_no_output) { time_t last_failure = 0; int failcount = failure_count(data_set, node, rsc, &last_failure); out->message(out, "resource-history", rsc, rsc_id, TRUE, failcount, last_failure, TRUE); rc = pcmk_rc_ok; } /* Print the operation */ out->message(out, "op-history", xml_op, task, interval_ms_s, op_rc_i, is_set(mon_ops, mon_op_print_timing)); } /* Free the list we created (no need to free the individual items) */ g_list_free(op_list); /* If we printed anything, close the resource */ if (rc == pcmk_rc_ok) { out->end_list(out); } return rc; } /*! * \internal * \brief Print node operation/failure history * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] node_state Root of XML tree describing node status. * \param[in] operations Whether to print operations or just failcounts. * \param[in] mon_ops Bitmask of mon_op_*. */ static int print_node_history(pcmk__output_t *out, pe_working_set_t *data_set, pe_node_t *node, xmlNode *node_state, gboolean operations, unsigned int mon_ops, GListPtr only_show) { xmlNode *lrm_rsc = NULL; xmlNode *rsc_entry = NULL; int rc = pcmk_rc_no_output; lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); /* Print history of each of the node's resources */ for (rsc_entry = __xml_first_child_element(lrm_rsc); rsc_entry != NULL; rsc_entry = __xml_next_element(rsc_entry)) { if (!crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) { continue; } if (operations == FALSE) { const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); pe_resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); time_t last_failure = 0; int failcount = failure_count(data_set, node, rsc, &last_failure); if (failcount > 0) { if (rc == pcmk_rc_no_output) { rc = pcmk_rc_ok; out->message(out, "node", node, get_resource_display_options(mon_ops), FALSE, NULL, is_set(mon_ops, mon_op_print_clone_detail), is_set(mon_ops, mon_op_print_brief), is_set(mon_ops, mon_op_group_by_node), only_show); } out->message(out, "resource-history", rsc, rsc_id, FALSE, failcount, last_failure, FALSE); } } else { GListPtr op_list = get_operation_list(rsc_entry); if (rc == pcmk_rc_no_output) { rc = pcmk_rc_ok; out->message(out, "node", node, get_resource_display_options(mon_ops), FALSE, NULL, is_set(mon_ops, mon_op_print_clone_detail), is_set(mon_ops, mon_op_print_brief), is_set(mon_ops, mon_op_group_by_node), only_show); } if (op_list != NULL) { print_rsc_history(out, data_set, node, rsc_entry, mon_ops, op_list); } } } if (rc == pcmk_rc_ok) { out->end_list(out); } return rc; } /*! * \internal * \brief Determine whether extended information about an attribute should be added. * * \param[in] out The output functions structure. * \param[in] node Node that ran this resource. * \param[in] rsc_list The list of resources for this node. * \param[in] attrname The attribute to find. * \param[out] expected_score The expected value for this attribute. * * \return TRUE if extended information should be printed, FALSE otherwise * \note Currently, extended information is only supported for ping/pingd * resources, for which a message will be printed if connectivity is lost * or degraded. */ static gboolean add_extra_info(pcmk__output_t *out, pe_node_t *node, GListPtr rsc_list, const char *attrname, int *expected_score) { GListPtr gIter = NULL; for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) { pe_resource_t *rsc = (pe_resource_t *) gIter->data; const char *type = g_hash_table_lookup(rsc->meta, "type"); const char *name = NULL; if (rsc->children != NULL) { if (add_extra_info(out, node, rsc->children, attrname, expected_score)) { return TRUE; } } if (safe_str_neq(type, "ping") && safe_str_neq(type, "pingd")) { return FALSE; } name = g_hash_table_lookup(rsc->parameters, "name"); if (name == NULL) { name = "pingd"; } /* To identify the resource with the attribute name. */ if (safe_str_eq(name, attrname)) { int host_list_num = 0; /* int value = crm_parse_int(attrvalue, "0"); */ const char *hosts = g_hash_table_lookup(rsc->parameters, "host_list"); const char *multiplier = g_hash_table_lookup(rsc->parameters, "multiplier"); if (hosts) { char **host_list = g_strsplit(hosts, " ", 0); host_list_num = g_strv_length(host_list); g_strfreev(host_list); } /* pingd multiplier is the same as the default value. */ *expected_score = host_list_num * crm_parse_int(multiplier, "1"); return TRUE; } } return FALSE; } /* structure for passing multiple user data to g_list_foreach() */ struct mon_attr_data { pcmk__output_t *out; pe_node_t *node; }; static void print_node_attribute(gpointer name, gpointer user_data) { const char *value = NULL; int expected_score = 0; gboolean add_extra = FALSE; struct mon_attr_data *data = (struct mon_attr_data *) user_data; value = pe_node_attribute_raw(data->node, name); add_extra = add_extra_info(data->out, data->node, data->node->details->running_rsc, name, &expected_score); /* Print attribute name and value */ data->out->message(data->out, "node-attribute", name, value, add_extra, expected_score); } /*! * \internal * \brief Print history for all nodes. * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] operations Whether to print operations or just failcounts. * \param[in] mon_ops Bitmask of mon_op_*. */ static int print_node_summary(pcmk__output_t *out, pe_working_set_t * data_set, gboolean operations, unsigned int mon_ops, GListPtr only_show, gboolean print_spacer) { xmlNode *node_state = NULL; xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input); int rc = pcmk_rc_no_output; if (xmlChildElementCount(cib_status) == 0) { return rc; } /* Print each node in the CIB status */ for (node_state = __xml_first_child_element(cib_status); node_state != NULL; node_state = __xml_next_element(node_state)) { pe_node_t *node; if (!crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { continue; } node = pe_find_node_id(data_set->nodes, ID(node_state)); if (!node || !node->details || !node->details->online) { continue; } if (!pcmk__str_in_list(only_show, node->details->uname)) { continue; } if (rc == pcmk_rc_no_output) { /* Add a blank line between this section and the one before it. */ if (print_spacer) { out->info(out, "%s", ""); } if (operations) { out->begin_list(out, NULL, NULL, "Operations"); } else { out->begin_list(out, NULL, NULL, "Migration Summary"); } rc = pcmk_rc_ok; } print_node_history(out, data_set, node, node_state, operations, mon_ops, only_show); } if (rc == pcmk_rc_ok) { out->end_list(out); } return rc; } /*! * \internal * \brief Print all tickets. * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. */ static int print_cluster_tickets(pcmk__output_t *out, pe_working_set_t * data_set, gboolean print_spacer) { GHashTableIter iter; gpointer key, value; if (g_hash_table_size(data_set->tickets) == 0) { return pcmk_rc_no_output; } /* Add a blank line between this section and the one before it. */ if (print_spacer) { out->info(out, "%s", ""); } /* Print section heading */ out->begin_list(out, NULL, NULL, "Tickets"); /* Print each ticket */ g_hash_table_iter_init(&iter, data_set->tickets); while (g_hash_table_iter_next(&iter, &key, &value)) { pe_ticket_t *ticket = (pe_ticket_t *) value; out->message(out, "ticket", ticket); } /* Close section */ out->end_list(out); return pcmk_rc_ok; } /*! * \internal * \brief Print section for negative location constraints * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] mon_ops Bitmask of mon_op_*. * \param[in] prefix ID prefix to filter results by. */ static int print_neg_locations(pcmk__output_t *out, pe_working_set_t *data_set, unsigned int mon_ops, const char *prefix, gboolean print_spacer) { GListPtr gIter, gIter2; int rc = pcmk_rc_no_output; /* Print each ban */ for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) { pe__location_t *location = gIter->data; if (prefix != NULL && !g_str_has_prefix(location->id, prefix)) continue; for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) { pe_node_t *node = (pe_node_t *) gIter2->data; if (node->weight < 0) { if (rc == pcmk_rc_no_output) { /* Add a blank line between this section and the one before it. */ if (print_spacer) { out->info(out, "%s", ""); } rc = pcmk_rc_ok; out->begin_list(out, NULL, NULL, "Negative Location Constraints"); } out->message(out, "ban", node, location, is_set(mon_ops, mon_op_print_clone_detail)); } } } if (rc == pcmk_rc_ok) { out->end_list(out); } return rc; } /*! * \internal * \brief Print node attributes section * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] mon_ops Bitmask of mon_op_*. */ static int print_node_attributes(pcmk__output_t *out, pe_working_set_t *data_set, unsigned int mon_ops, GListPtr only_show, gboolean print_spacer) { GListPtr gIter = NULL; int rc = pcmk_rc_no_output; /* Unpack all resource parameters (it would be more efficient to do this * only when needed for the first time in add_extra_info()) */ for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) { crm_mon_get_parameters(gIter->data, data_set); } /* Display each node's attributes */ for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { struct mon_attr_data data; data.out = out; data.node = (pe_node_t *) gIter->data; if (data.node && data.node->details && data.node->details->online) { GList *attr_list = NULL; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, data.node->details->attrs); while (g_hash_table_iter_next (&iter, &key, &value)) { attr_list = append_attr_list(attr_list, key); } if (attr_list == NULL) { continue; } if (!pcmk__str_in_list(only_show, data.node->details->uname)) { continue; } if (rc == pcmk_rc_no_output) { /* Add a blank line between this section and the one before it. */ if (print_spacer) { out->info(out, "%s", ""); } rc = pcmk_rc_ok; out->begin_list(out, NULL, NULL, "Node Attributes"); } out->message(out, "node", data.node, get_resource_display_options(mon_ops), FALSE, NULL, is_set(mon_ops, mon_op_print_clone_detail), is_set(mon_ops, mon_op_print_brief), is_set(mon_ops, mon_op_group_by_node), only_show); g_list_foreach(attr_list, print_node_attribute, &data); g_list_free(attr_list); out->end_list(out); } } /* Print section footer */ if (rc == pcmk_rc_ok) { out->end_list(out); } return rc; } /*! * \internal * \brief Print a section for failed actions * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. */ static int print_failed_actions(pcmk__output_t *out, pe_working_set_t *data_set, GListPtr only_show, gboolean print_spacer) { xmlNode *xml_op = NULL; int rc = pcmk_rc_no_output; if (xmlChildElementCount(data_set->failed) == 0) { return rc; } for (xml_op = __xml_first_child(data_set->failed); xml_op != NULL; xml_op = __xml_next(xml_op)) { if (!pcmk__str_in_list(only_show, crm_element_value(xml_op, XML_ATTR_UNAME))) { continue; } if (rc == pcmk_rc_no_output) { /* Add a blank line between this section and the one before it. */ if (print_spacer) { out->info(out, "%s", ""); } rc = pcmk_rc_ok; out->begin_list(out, NULL, NULL, "Failed Resource Actions"); } out->message(out, "failed-action", xml_op); } if (rc == pcmk_rc_ok) { out->end_list(out); } return rc; } /*! * \internal * \brief Top-level printing function for text/curses output. * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] stonith_history List of stonith actions. * \param[in] mon_ops Bitmask of mon_op_*. * \param[in] show Bitmask of mon_show_*. * \param[in] prefix ID prefix to filter results by. */ void print_status(pcmk__output_t *out, pe_working_set_t *data_set, stonith_history_t *stonith_history, unsigned int mon_ops, unsigned int show, char *prefix, char *only_show) { GListPtr unames = NULL; - GListPtr gIter = NULL; unsigned int print_opts = get_resource_display_options(mon_ops); int rc = pcmk_rc_no_output; + int x; - /* space-separated lists of node names */ - char *online_nodes = NULL; - char *online_remote_nodes = NULL; - char *online_guest_nodes = NULL; - char *offline_nodes = NULL; - char *offline_remote_nodes = NULL; - - rc = out->message(out, "cluster-summary", data_set, - is_set(mon_ops, mon_op_print_clone_detail), - is_set(show, mon_show_stack), - is_set(show, mon_show_dc), - is_set(show, mon_show_times), - is_set(show, mon_show_counts), - is_set(show, mon_show_options)); + x = out->message(out, "cluster-summary", data_set, + is_set(mon_ops, mon_op_print_clone_detail), + is_set(show, mon_show_stack), + is_set(show, mon_show_dc), + is_set(show, mon_show_times), + is_set(show, mon_show_counts), + is_set(show, mon_show_options)); + + if (x == pcmk_rc_ok) { + rc = pcmk_rc_ok; + } unames = build_uname_list(data_set, only_show); - /* Gather node information (and print if in bad state or grouping by node) */ if (is_set(show, mon_show_nodes)) { - gboolean printed_header = FALSE; - - for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { - pe_node_t *node = (pe_node_t *) gIter->data; - const char *node_mode = NULL; - char *node_name = pe__node_display_name(node, is_set(mon_ops, mon_op_print_clone_detail)); - - if (!pcmk__str_in_list(unames, node->details->uname)) { - free(node_name); - continue; - } - - if (printed_header == FALSE) { - if (rc == pcmk_rc_ok) { - out->info(out, "%s", ""); - } else { - rc = pcmk_rc_ok; - } - - out->begin_list(out, NULL, NULL, "Node List"); - printed_header = TRUE; - } - - /* Get node mode */ - if (node->details->unclean) { - if (node->details->online) { - node_mode = "UNCLEAN (online)"; - - } else if (node->details->pending) { - node_mode = "UNCLEAN (pending)"; - - } else { - node_mode = "UNCLEAN (offline)"; - } - - } else if (node->details->pending) { - node_mode = "pending"; - - } else if (node->details->standby_onfail && node->details->online) { - node_mode = "standby (on-fail)"; - - } else if (node->details->standby) { - if (node->details->online) { - if (node->details->running_rsc) { - node_mode = "standby (with active resources)"; - } else { - node_mode = "standby"; - } - } else { - node_mode = "OFFLINE (standby)"; - } - - } else if (node->details->maintenance) { - if (node->details->online) { - node_mode = "maintenance"; - } else { - node_mode = "OFFLINE (maintenance)"; - } - - } else if (node->details->online) { - node_mode = "online"; - if (is_not_set(mon_ops, mon_op_group_by_node)) { - if (pe__is_guest_node(node)) { - online_guest_nodes = pcmk__add_word(online_guest_nodes, - node_name); - } else if (pe__is_remote_node(node)) { - online_remote_nodes = pcmk__add_word(online_remote_nodes, - node_name); - } else { - online_nodes = pcmk__add_word(online_nodes, node_name); - } - free(node_name); - continue; - } - - } else { - node_mode = "OFFLINE"; - if (is_not_set(mon_ops, mon_op_group_by_node)) { - if (pe__is_remote_node(node)) { - offline_remote_nodes = pcmk__add_word(offline_remote_nodes, - node_name); - } else if (pe__is_guest_node(node)) { - /* ignore offline guest nodes */ - } else { - offline_nodes = pcmk__add_word(offline_nodes, node_name); - } - free(node_name); - continue; - } - } - - /* If we get here, node is in bad state, or we're grouping by node */ - out->message(out, "node", node, get_resource_display_options(mon_ops), TRUE, - node_mode, is_set(mon_ops, mon_op_print_clone_detail), - is_set(mon_ops, mon_op_print_brief), is_set(mon_ops, mon_op_group_by_node), - unames); - free(node_name); + if (rc == pcmk_rc_ok) { + out->info(out, "%s", ""); } - /* If we're not grouping by node, summarize nodes by status */ - if (online_nodes) { - out->list_item(out, "Online", "[%s ]", online_nodes); - free(online_nodes); - } - if (offline_nodes) { - out->list_item(out, "OFFLINE", "[%s ]", offline_nodes); - free(offline_nodes); - } - if (online_remote_nodes) { - out->list_item(out, "RemoteOnline", "[%s ]", online_remote_nodes); - free(online_remote_nodes); - } - if (offline_remote_nodes) { - out->list_item(out, "RemoteOFFLINE", "[%s ]", offline_remote_nodes); - free(offline_remote_nodes); - } - if (online_guest_nodes) { - out->list_item(out, "GuestOnline", "[%s ]", online_guest_nodes); - free(online_guest_nodes); - } + x = out->message(out, "node-list", data_set->nodes, unames, mon_ops, + print_opts, is_set(mon_ops, mon_op_print_clone_detail), + is_set(mon_ops, mon_op_print_brief), + is_set(mon_ops, mon_op_group_by_node)); - if (printed_header == TRUE) { - out->end_list(out); + if (x == pcmk_rc_ok) { + rc = pcmk_rc_ok; } } /* Print resources section, if needed */ if (is_set(show, mon_show_resources)) { - int x = print_resources(out, data_set, print_opts, mon_ops, - is_set(mon_ops, mon_op_print_brief), TRUE, - unames, rc == pcmk_rc_ok); + x = print_resources(out, data_set, print_opts, mon_ops, + is_set(mon_ops, mon_op_print_brief), TRUE, + unames, rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } } /* print Node Attributes section if requested */ if (is_set(show, mon_show_attributes)) { - int x = print_node_attributes(out, data_set, mon_ops, unames, - rc == pcmk_rc_ok); + x = print_node_attributes(out, data_set, mon_ops, unames, + rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } } /* If requested, print resource operations (which includes failcounts) * or just failcounts */ if (is_set(show, mon_show_operations) || is_set(show, mon_show_failcounts)) { - int x = print_node_summary(out, data_set, is_set(show, mon_show_operations), - mon_ops, unames, rc == pcmk_rc_ok); + x = print_node_summary(out, data_set, is_set(show, mon_show_operations), + mon_ops, unames, rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } } /* If there were any failed actions, print them */ if (is_set(show, mon_show_failures) && xml_has_children(data_set->failed)) { - int x = print_failed_actions(out, data_set, unames, rc == pcmk_rc_ok); + x = print_failed_actions(out, data_set, unames, rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } } /* Print failed stonith actions */ if (is_set(show, mon_show_fence_failed) && is_set(mon_ops, mon_op_fence_history)) { for (stonith_history_t *hp = stonith_history; hp; hp = hp->next) { if (hp->state == st_failed) { - int x = out->message(out, "failed-fencing-history", hp, unames, - is_set(mon_ops, mon_op_fence_full_history), - rc == pcmk_rc_ok); + x = out->message(out, "failed-fencing-history", hp, unames, + is_set(mon_ops, mon_op_fence_full_history), + rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } break; } } } /* Print tickets if requested */ if (is_set(show, mon_show_tickets)) { - int x = print_cluster_tickets(out, data_set, rc == pcmk_rc_ok); + x = print_cluster_tickets(out, data_set, rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } } /* Print negative location constraints if requested */ if (is_set(show, mon_show_bans)) { - int x = print_neg_locations(out, data_set, mon_ops, prefix, rc == pcmk_rc_ok); + x = print_neg_locations(out, data_set, mon_ops, prefix, rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } } /* Print stonith history */ if (is_set(mon_ops, mon_op_fence_history)) { if (is_set(show, mon_show_fence_worked)) { for (stonith_history_t *hp = stonith_history; hp; hp = hp->next) { if (hp->state != st_failed) { - int x = out->message(out, "fencing-history", hp, unames, - is_set(mon_ops, mon_op_fence_full_history), - rc == pcmk_rc_ok); + x = out->message(out, "fencing-history", hp, unames, + is_set(mon_ops, mon_op_fence_full_history), + rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } break; } } } else if (is_set(show, mon_show_fence_pending)) { for (stonith_history_t *hp = stonith_history; hp; hp = hp->next) { if (hp->state != st_failed && hp->state != st_done) { - int x = out->message(out, "pending-fencing-actions", hp, unames, - is_set(mon_ops, mon_op_fence_full_history), - rc == pcmk_rc_ok); + x = out->message(out, "pending-fencing-actions", hp, unames, + is_set(mon_ops, mon_op_fence_full_history), + rc == pcmk_rc_ok); if (x == pcmk_rc_ok) { rc = pcmk_rc_ok; } break; } } } } g_list_free_full(unames, free); } /*! * \internal * \brief Top-level printing function for XML output. * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] stonith_history List of stonith actions. * \param[in] mon_ops Bitmask of mon_op_*. * \param[in] show Bitmask of mon_show_*. * \param[in] prefix ID prefix to filter results by. */ void print_xml_status(pcmk__output_t *out, pe_working_set_t *data_set, crm_exit_t history_rc, stonith_history_t *stonith_history, unsigned int mon_ops, unsigned int show, char *prefix, char *only_show) { GListPtr unames = NULL; - - GListPtr gIter = NULL; unsigned int print_opts = get_resource_display_options(mon_ops); out->message(out, "cluster-summary", data_set, is_set(mon_ops, mon_op_print_clone_detail), is_set(show, mon_show_stack), is_set(show, mon_show_dc), is_set(show, mon_show_times), is_set(show, mon_show_counts), is_set(show, mon_show_options)); unames = build_uname_list(data_set, only_show); /*** NODES ***/ if (is_set(show, mon_show_nodes)) { - out->begin_list(out, NULL, NULL, "nodes"); - for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { - pe_node_t *node = (pe_node_t *) gIter->data; - - if (!pcmk__str_in_list(unames, node->details->uname)) { - continue; - } - - out->message(out, "node", node, get_resource_display_options(mon_ops), TRUE, - NULL, is_set(mon_ops, mon_op_print_clone_detail), - is_set(mon_ops, mon_op_print_brief), is_set(mon_ops, mon_op_group_by_node), - only_show); - } - out->end_list(out); + out->message(out, "node-list", data_set->nodes, unames, mon_ops, + print_opts, is_set(mon_ops, mon_op_print_clone_detail), + is_set(mon_ops, mon_op_print_brief), + is_set(mon_ops, mon_op_group_by_node)); } /* Print resources section, if needed */ if (is_set(show, mon_show_resources)) { print_resources(out, data_set, print_opts, mon_ops, FALSE, FALSE, unames, FALSE); } /* print Node Attributes section if requested */ if (is_set(show, mon_show_attributes)) { print_node_attributes(out, data_set, mon_ops, unames, FALSE); } /* If requested, print resource operations (which includes failcounts) * or just failcounts */ if (is_set(show, mon_show_operations) || is_set(show, mon_show_failcounts)) { print_node_summary(out, data_set, is_set(show, mon_show_operations), mon_ops, unames, FALSE); } /* If there were any failed actions, print them */ if (is_set(show, mon_show_failures) && xml_has_children(data_set->failed)) { print_failed_actions(out, data_set, unames, FALSE); } /* Print stonith history */ if (is_set(show, mon_show_fencing_all) && is_set(mon_ops, mon_op_fence_history)) { out->message(out, "full-fencing-history", history_rc, stonith_history, unames, is_set(mon_ops, mon_op_fence_full_history), FALSE); } /* Print tickets if requested */ if (is_set(show, mon_show_tickets)) { print_cluster_tickets(out, data_set, FALSE); } /* Print negative location constraints if requested */ if (is_set(show, mon_show_bans)) { print_neg_locations(out, data_set, mon_ops, prefix, FALSE); } g_list_free_full(unames, free); } /*! * \internal * \brief Top-level printing function for HTML output. * * \param[in] out The output functions structure. * \param[in] data_set Cluster state to display. * \param[in] stonith_history List of stonith actions. * \param[in] mon_ops Bitmask of mon_op_*. * \param[in] show Bitmask of mon_show_*. * \param[in] prefix ID prefix to filter results by. */ int print_html_status(pcmk__output_t *out, pe_working_set_t *data_set, stonith_history_t *stonith_history, unsigned int mon_ops, unsigned int show, char *prefix, char *only_show) { GListPtr unames = NULL; - - GListPtr gIter = NULL; unsigned int print_opts = get_resource_display_options(mon_ops); out->message(out, "cluster-summary", data_set, is_set(mon_ops, mon_op_print_clone_detail), is_set(show, mon_show_stack), is_set(show, mon_show_dc), is_set(show, mon_show_times), is_set(show, mon_show_counts), is_set(show, mon_show_options)); unames = build_uname_list(data_set, only_show); /*** NODE LIST ***/ if (is_set(show, mon_show_nodes)) { - gboolean printed_header = FALSE; - - for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { - pe_node_t *node = (pe_node_t *) gIter->data; - - if (!pcmk__str_in_list(unames, node->details->uname)) { - continue; - } - - if (printed_header == FALSE) { - printed_header = TRUE; - out->begin_list(out, NULL, NULL, "Node List"); - } - - out->message(out, "node", node, get_resource_display_options(mon_ops), TRUE, - NULL, is_set(mon_ops, mon_op_print_clone_detail), - is_set(mon_ops, mon_op_print_brief), is_set(mon_ops, mon_op_group_by_node), - only_show); - } - - if (printed_header == TRUE) { - out->end_list(out); - } + out->message(out, "node-list", data_set->nodes, unames, mon_ops, + print_opts, is_set(mon_ops, mon_op_print_clone_detail), + is_set(mon_ops, mon_op_print_brief), + is_set(mon_ops, mon_op_group_by_node)); } /* Print resources section, if needed */ if (is_set(show, mon_show_resources)) { print_resources(out, data_set, print_opts, mon_ops, is_set(mon_ops, mon_op_print_brief), TRUE, unames, FALSE); } /* print Node Attributes section if requested */ if (is_set(show, mon_show_attributes)) { print_node_attributes(out, data_set, mon_ops, unames, FALSE); } /* If requested, print resource operations (which includes failcounts) * or just failcounts */ if (is_set(show, mon_show_operations) || is_set(show, mon_show_failcounts)) { print_node_summary(out, data_set, is_set(show, mon_show_operations), mon_ops, unames, FALSE); } /* If there were any failed actions, print them */ if (is_set(show, mon_show_failures) && xml_has_children(data_set->failed)) { print_failed_actions(out, data_set, unames, FALSE); } /* Print failed stonith actions */ if (is_set(show, mon_show_fence_failed) && is_set(mon_ops, mon_op_fence_history)) { for (stonith_history_t *hp = stonith_history; hp; hp = hp->next) { if (hp->state == st_failed) { out->message(out, "failed-fencing-history", hp, unames, is_set(mon_ops, mon_op_fence_full_history), FALSE); break; } } } /* Print stonith history */ if (is_set(mon_ops, mon_op_fence_history)) { if (is_set(show, mon_show_fence_worked)) { for (stonith_history_t *hp = stonith_history; hp; hp = hp->next) { if (hp->state != st_failed) { out->message(out, "fencing-history", hp, unames, is_set(mon_ops, mon_op_fence_full_history), FALSE); break; } } } else if (is_set(show, mon_show_fence_pending)) { for (stonith_history_t *hp = stonith_history; hp; hp = hp->next) { if (hp->state != st_failed && hp->state != st_done) { out->message(out, "pending-fencing-actions", hp, unames, is_set(mon_ops, mon_op_fence_full_history), FALSE); break; } } } } /* Print tickets if requested */ if (is_set(show, mon_show_tickets)) { print_cluster_tickets(out, data_set, FALSE); } /* Print negative location constraints if requested */ if (is_set(show, mon_show_bans)) { print_neg_locations(out, data_set, mon_ops, prefix, FALSE); } g_list_free_full(unames, free); return 0; }