Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4525319
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
127 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/include/crm/common/internal.h b/include/crm/common/internal.h
index e722cd720f..beb19c3b0d 100644
--- a/include/crm/common/internal.h
+++ b/include/crm/common/internal.h
@@ -1,351 +1,353 @@
/*
* Copyright 2015-2021 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 CRM_COMMON_INTERNAL__H
#define CRM_COMMON_INTERNAL__H
#include <unistd.h> // getpid()
#include <stdbool.h> // bool
#include <stdint.h> // uint8_t, uint64_t
#include <string.h> // strcmp()
#include <fcntl.h> // open()
#include <sys/types.h> // uid_t, gid_t, pid_t
#include <glib.h> // guint, GList, GHashTable
#include <libxml/tree.h> // xmlNode
#include <crm/common/util.h> // crm_strdup_printf()
#include <crm/common/logging.h> // do_crm_log_unlikely(), etc.
#include <crm/common/mainloop.h> // mainloop_io_t, struct ipc_client_callbacks
#include <crm/common/strings_internal.h>
// Number of elements in a statically defined array
#define PCMK__NELEM(a) ((int) (sizeof(a)/sizeof(a[0])) )
// Internal ACL-related utilities (from acl.c)
char *pcmk__uid2username(uid_t uid);
const char *pcmk__update_acl_user(xmlNode *request, const char *field,
const char *peer_user);
static inline bool
pcmk__is_privileged(const char *user)
{
return user && (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root"));
}
#if SUPPORT_CIBSECRETS
// Internal CIB utilities (from cib_secrets.c) */
int pcmk__substitute_secrets(const char *rsc_id, GHashTable *params);
#endif
/* internal digest-related utilities (from digest.c) */
bool pcmk__verify_digest(xmlNode *input, const char *expected);
/* internal I/O utilities (from io.c) */
int pcmk__real_path(const char *path, char **resolved_path);
char *pcmk__series_filename(const char *directory, const char *series,
int sequence, bool bzip);
int pcmk__read_series_sequence(const char *directory, const char *series,
unsigned int *seq);
void pcmk__write_series_sequence(const char *directory, const char *series,
unsigned int sequence, int max);
int pcmk__chown_series_sequence(const char *directory, const char *series,
uid_t uid, gid_t gid);
int pcmk__build_path(const char *path_c, mode_t mode);
bool pcmk__daemon_can_write(const char *dir, const char *file);
void pcmk__sync_directory(const char *name);
int pcmk__file_contents(const char *filename, char **contents);
int pcmk__write_sync(int fd, const char *contents);
int pcmk__set_nonblocking(int fd);
const char *pcmk__get_tmpdir(void);
void pcmk__close_fds_in_child(bool);
/*!
* \internal
* \brief Open /dev/null to consume next available file descriptor
*
* Open /dev/null, disregarding the result. This is intended when daemonizing to
* be able to null stdin, stdout, and stderr.
*
* \param[in] flags O_RDONLY (stdin) or O_WRONLY (stdout and stderr)
*/
static inline void
pcmk__open_devnull(int flags)
{
// Static analysis clutter
// cppcheck-suppress leakReturnValNotUsed
(void) open("/dev/null", flags);
}
/* internal main loop utilities (from mainloop.c) */
int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata,
struct ipc_client_callbacks *callbacks,
mainloop_io_t **source);
guint pcmk__mainloop_timer_get_period(mainloop_timer_t *timer);
/* internal messaging utilities (from messages.c) */
const char *pcmk__message_name(const char *name);
/* internal name/value utilities (from nvpair.c) */
int pcmk__scan_nvpair(const char *input, char **name, char **value);
+char *pcmk__format_nvpair(const char *name, const char *value,
+ const char *units);
/* internal procfs utilities (from procfs.c) */
pid_t pcmk__procfs_pid_of(const char *name);
unsigned int pcmk__procfs_num_cores(void);
/* internal XML schema functions (from xml.c) */
void crm_schema_init(void);
void crm_schema_cleanup(void);
/* internal functions related to process IDs (from pid.c) */
/*!
* \internal
* \brief Check whether process exists (by PID and optionally executable path)
*
* \param[in] pid PID of process to check
* \param[in] daemon If not NULL, path component to match with procfs entry
*
* \return Standard Pacemaker return code
* \note Particular return codes of interest include pcmk_rc_ok for alive,
* ESRCH for process is not alive (verified by kill and/or executable path
* match), EACCES for caller unable or not allowed to check. A result of
* "alive" is less reliable when \p daemon is not provided or procfs is
* not available, since there is no guarantee that the PID has not been
* recycled for another process.
* \note This function cannot be used to verify \e authenticity of the process.
*/
int pcmk__pid_active(pid_t pid, const char *daemon);
int pcmk__read_pidfile(const char *filename, pid_t *pid);
int pcmk__pidfile_matches(const char *filename, pid_t expected_pid,
const char *expected_name, pid_t *pid);
int pcmk__lock_pidfile(const char *filename, const char *name);
/* internal functions related to resource operations (from operations.c) */
// printf-style format to create operation ID from resource, action, interval
#define PCMK__OP_FMT "%s_%s_%u"
char *pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms);
char *pcmk__notify_key(const char *rsc_id, const char *notify_type,
const char *op_type);
char *pcmk__transition_key(int transition_id, int action_id, int target_rc,
const char *node);
void pcmk__filter_op_for_digest(xmlNode *param_set);
// bitwise arithmetic utilities
/*!
* \internal
* \brief Set specified flags in a flag group
*
* \param[in] function Function name of caller
* \param[in] line Line number of caller
* \param[in] log_level Log a message at this level
* \param[in] flag_type Label describing this flag group (for logging)
* \param[in] target Name of object whose flags these are (for logging)
* \param[in] flag_group Flag group being manipulated
* \param[in] flags Which flags in the group should be set
* \param[in] flags_str Readable equivalent of \p flags (for logging)
*
* \return Possibly modified flag group
*/
static inline uint64_t
pcmk__set_flags_as(const char *function, int line, uint8_t log_level,
const char *flag_type, const char *target,
uint64_t flag_group, uint64_t flags, const char *flags_str)
{
uint64_t result = flag_group | flags;
if (result != flag_group) {
do_crm_log_unlikely(log_level,
"%s flags 0x%.8llx (%s) for %s set by %s:%d",
((flag_type == NULL)? "Group of" : flag_type),
(unsigned long long) flags,
((flags_str == NULL)? "flags" : flags_str),
((target == NULL)? "target" : target),
function, line);
}
return result;
}
/*!
* \internal
* \brief Clear specified flags in a flag group
*
* \param[in] function Function name of caller
* \param[in] line Line number of caller
* \param[in] log_level Log a message at this level
* \param[in] flag_type Label describing this flag group (for logging)
* \param[in] target Name of object whose flags these are (for logging)
* \param[in] flag_group Flag group being manipulated
* \param[in] flags Which flags in the group should be cleared
* \param[in] flags_str Readable equivalent of \p flags (for logging)
*
* \return Possibly modified flag group
*/
static inline uint64_t
pcmk__clear_flags_as(const char *function, int line, uint8_t log_level,
const char *flag_type, const char *target,
uint64_t flag_group, uint64_t flags, const char *flags_str)
{
uint64_t result = flag_group & ~flags;
if (result != flag_group) {
do_crm_log_unlikely(log_level,
"%s flags 0x%.8llx (%s) for %s cleared by %s:%d",
((flag_type == NULL)? "Group of" : flag_type),
(unsigned long long) flags,
((flags_str == NULL)? "flags" : flags_str),
((target == NULL)? "target" : target),
function, line);
}
return result;
}
// miscellaneous utilities (from utils.c)
void pcmk__daemonize(const char *name, const char *pidfile);
void pcmk__panic(const char *origin);
pid_t pcmk__locate_sbd(void);
void pcmk__sleep_ms(unsigned int ms);
extern int pcmk__score_red;
extern int pcmk__score_green;
extern int pcmk__score_yellow;
/*!
* \internal
* \brief Resize a dynamically allocated memory block
*
* \param[in] ptr Memory block to resize (or NULL to allocate new memory)
* \param[in] size New size of memory block in bytes (must be > 0)
*
* \return Pointer to resized memory block
*
* \note This asserts on error, so the result is guaranteed to be non-NULL
* (which is the main advantage of this over directly using realloc()).
*/
static inline void *
pcmk__realloc(void *ptr, size_t size)
{
void *new_ptr;
// realloc(p, 0) can replace free(p) but this wrapper can't
CRM_ASSERT(size > 0);
new_ptr = realloc(ptr, size);
if (new_ptr == NULL) {
free(ptr);
abort();
}
return new_ptr;
}
/* Error domains for use with g_set_error (from results.c) */
GQuark pcmk__rc_error_quark(void);
GQuark pcmk__exitc_error_quark(void);
#define PCMK__RC_ERROR pcmk__rc_error_quark()
#define PCMK__EXITC_ERROR pcmk__exitc_error_quark()
static inline char *
pcmk__getpid_s(void)
{
return crm_strdup_printf("%lu", (unsigned long) getpid());
}
// More efficient than g_list_length(list) == 1
static inline bool
pcmk__list_of_1(GList *list)
{
return list && (list->next == NULL);
}
// More efficient than g_list_length(list) > 1
static inline bool
pcmk__list_of_multiple(GList *list)
{
return list && (list->next != NULL);
}
/* convenience functions for failure-related node attributes */
#define PCMK__FAIL_COUNT_PREFIX "fail-count"
#define PCMK__LAST_FAILURE_PREFIX "last-failure"
/*!
* \internal
* \brief Generate a failure-related node attribute name for a resource
*
* \param[in] prefix Start of attribute name
* \param[in] rsc_id Resource name
* \param[in] op Operation name
* \param[in] interval_ms Operation interval
*
* \return Newly allocated string with attribute name
*
* \note Failure attributes are named like PREFIX-RSC#OP_INTERVAL (for example,
* "fail-count-myrsc#monitor_30000"). The '#' is used because it is not
* a valid character in a resource ID, to reliably distinguish where the
* operation name begins. The '_' is used simply to be more comparable to
* action labels like "myrsc_monitor_30000".
*/
static inline char *
pcmk__fail_attr_name(const char *prefix, const char *rsc_id, const char *op,
guint interval_ms)
{
CRM_CHECK(prefix && rsc_id && op, return NULL);
return crm_strdup_printf("%s-%s#%s_%u", prefix, rsc_id, op, interval_ms);
}
static inline char *
pcmk__failcount_name(const char *rsc_id, const char *op, guint interval_ms)
{
return pcmk__fail_attr_name(PCMK__FAIL_COUNT_PREFIX, rsc_id, op,
interval_ms);
}
static inline char *
pcmk__lastfailure_name(const char *rsc_id, const char *op, guint interval_ms)
{
return pcmk__fail_attr_name(PCMK__LAST_FAILURE_PREFIX, rsc_id, op,
interval_ms);
}
// internal resource agent functions (from agents.c)
int pcmk__effective_rc(int rc);
#endif /* CRM_COMMON_INTERNAL__H */
diff --git a/include/crm/common/util.h b/include/crm/common/util.h
index 0d3072717e..7a369d1aaa 100644
--- a/include/crm/common/util.h
+++ b/include/crm/common/util.h
@@ -1,216 +1,215 @@
/*
* Copyright 2004-2021 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 CRM_COMMON_UTIL__H
# define CRM_COMMON_UTIL__H
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief Utility functions
* \ingroup core
*/
# include <sys/types.h> // gid_t, mode_t, size_t, time_t, uid_t
# include <stdlib.h>
# include <stdbool.h>
# include <stdint.h> // uint32_t
# include <limits.h>
# include <signal.h>
# include <glib.h>
# include <libxml/tree.h>
# include <crm/lrmd.h>
# include <crm/common/acl.h>
# include <crm/common/agents.h>
# include <crm/common/results.h>
# define ONLINESTATUS "online" // Status of an online client
# define OFFLINESTATUS "offline" // Status of an offline client
// public name/value pair functions (from nvpair.c)
-char *pcmk_format_nvpair(const char *name, const char *value, const char *units);
char *pcmk_format_named_time(const char *name, time_t epoch_time);
/* public Pacemaker Remote functions (from remote.c) */
int crm_default_remote_port(void);
/* public string functions (from strings.c) */
char *crm_itoa_stack(int an_int, char *buf, size_t len);
gboolean crm_is_true(const char *s);
int crm_str_to_boolean(const char *s, int *ret);
long long crm_parse_ll(const char *text, const char *default_text);
int crm_parse_int(const char *text, const char *default_text);
long long crm_get_msec(const char *input);
char * crm_strip_trailing_newline(char *str);
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b);
guint crm_strcase_hash(gconstpointer v);
guint g_str_hash_traditional(gconstpointer v);
char *crm_strdup_printf(char const *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
int pcmk_numeric_strcasecmp(const char *s1, const char *s2);
# define crm_str_hash g_str_hash_traditional
static inline char *
crm_itoa(int an_int)
{
return crm_strdup_printf("%d", an_int);
}
static inline char *
crm_ftoa(double a_float)
{
return crm_strdup_printf("%f", a_float);
}
static inline char *
crm_ttoa(time_t epoch_time)
{
return crm_strdup_printf("%lld", (long long) epoch_time);
}
/*!
* \brief Create hash table with dynamically allocated string keys/values
*
* \return Newly allocated hash table
* \note It is the caller's responsibility to free the result, using
* g_hash_table_destroy().
*/
static inline GHashTable *
crm_str_table_new(void)
{
return g_hash_table_new_full(crm_str_hash, g_str_equal, free, free);
}
/*!
* \brief Create hash table with case-insensitive dynamically allocated string keys/values
*
* \return Newly allocated hash table
* \note It is the caller's responsibility to free the result, using
* g_hash_table_destroy().
*/
static inline GHashTable *
crm_strcase_table_new(void)
{
return g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, free, free);
}
GHashTable *crm_str_table_dup(GHashTable *old_table);
# define crm_atoi(text, default_text) crm_parse_int(text, default_text)
/* public I/O functions (from io.c) */
void crm_build_path(const char *path_c, mode_t mode);
guint crm_parse_interval_spec(const char *input);
int char2score(const char *score);
char *score2char(int score);
char *score2char_stack(int score, char *buf, size_t len);
/* public operation functions (from operations.c) */
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type,
guint *interval_ms);
gboolean decode_transition_key(const char *key, char **uuid, int *transition_id,
int *action_id, int *target_rc);
gboolean decode_transition_magic(const char *magic, char **uuid,
int *transition_id, int *action_id,
int *op_status, int *op_rc, int *target_rc);
int rsc_op_expected_rc(lrmd_event_data_t *event);
gboolean did_rsc_op_fail(lrmd_event_data_t *event, int target_rc);
bool crm_op_needs_metadata(const char *rsc_class, const char *op);
xmlNode *crm_create_op_xml(xmlNode *parent, const char *prefix,
const char *task, const char *interval_spec,
const char *timeout);
#define CRM_DEFAULT_OP_TIMEOUT_S "20s"
int compare_version(const char *version1, const char *version2);
/* coverity[+kill] */
void crm_abort(const char *file, const char *function, int line,
const char *condition, gboolean do_core, gboolean do_fork);
/*!
* \brief Check whether any of specified flags are set in a flag group
*
* \param[in] flag_group The flag group being examined
* \param[in] flags_to_check Which flags in flag_group should be checked
*
* \return true if \p flags_to_check is nonzero and any of its flags are set in
* \p flag_group, or false otherwise
*/
static inline bool
pcmk_any_flags_set(uint64_t flag_group, uint64_t flags_to_check)
{
return (flag_group & flags_to_check) != 0;
}
/*!
* \brief Check whether all of specified flags are set in a flag group
*
* \param[in] flag_group The flag group being examined
* \param[in] flags_to_check Which flags in flag_group should be checked
*
* \return true if \p flags_to_check is zero or all of its flags are set in
* \p flag_group, or false otherwise
*/
static inline bool
pcmk_all_flags_set(uint64_t flag_group, uint64_t flags_to_check)
{
return (flag_group & flags_to_check) == flags_to_check;
}
/*!
* \brief Convenience alias for pcmk_all_flags_set(), to check single flag
*/
#define pcmk_is_set(g, f) pcmk_all_flags_set((g), (f))
static inline guint
crm_hash_table_size(GHashTable * hashtable)
{
if (hashtable == NULL) {
return 0;
}
return g_hash_table_size(hashtable);
}
char *crm_meta_name(const char *field);
const char *crm_meta_value(GHashTable * hash, const char *field);
char *crm_md5sum(const char *buffer);
char *crm_generate_uuid(void);
// This belongs in ipc.h but is here for backward compatibility
bool crm_is_daemon_name(const char *name);
int crm_user_lookup(const char *name, uid_t * uid, gid_t * gid);
int pcmk_daemon_user(uid_t *uid, gid_t *gid);
#ifdef HAVE_GNUTLS_GNUTLS_H
void crm_gnutls_global_init(void);
#endif
char *pcmk_hostname(void);
bool pcmk_str_is_infinity(const char *s);
bool pcmk_str_is_minus_infinity(const char *s);
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
#include <crm/common/util_compat.h>
#endif
#ifdef __cplusplus
}
#endif
#endif
diff --git a/include/crm/common/util_compat.h b/include/crm/common/util_compat.h
index 96ba0584e4..0b97c36cc7 100644
--- a/include/crm/common/util_compat.h
+++ b/include/crm/common/util_compat.h
@@ -1,66 +1,70 @@
/*
* Copyright 2004-2021 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 PCMK__COMMON_UTIL_COMPAT__H
# define PCMK__COMMON_UTIL_COMPAT__H
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief Deprecated Pacemaker utilities
* \ingroup core
* \deprecated Do not include this header directly. The utilities in this
* header, and the header itself, will be removed in a future
* release.
*/
//! \deprecated Use crm_parse_interval_spec() instead
#define crm_get_interval crm_parse_interval_spec
//! \deprecated Use !pcmk_is_set() or !pcmk_all_flags_set() instead
static inline gboolean
is_not_set(long long word, long long bit)
{
return ((word & bit) == 0);
}
//! \deprecated Use pcmk_is_set() or pcmk_all_flags_set() instead
static inline gboolean
is_set(long long word, long long bit)
{
return ((word & bit) == bit);
}
//! \deprecated Use pcmk_any_flags_set() instead
static inline gboolean
is_set_any(long long word, long long bit)
{
return ((word & bit) != 0);
}
//! \deprecated Use strcmp() or strcasecmp() instead
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case);
//! \deprecated Use strcmp() instead
gboolean safe_str_neq(const char *a, const char *b);
//! \deprecated Use strcasecmp() instead
#define safe_str_eq(a, b) crm_str_eq(a, b, FALSE)
//! \deprecated Use sscanf() instead
int pcmk_scan_nvpair(const char *input, char **name, char **value);
+//! \deprecated Use a standard printf()-style function instead
+char *pcmk_format_nvpair(const char *name, const char *value,
+ const char *units);
+
#ifdef __cplusplus
}
#endif
#endif // PCMK__COMMON_UTIL_COMPAT__H
diff --git a/lib/common/nvpair.c b/lib/common/nvpair.c
index 0473c144d2..2a12c105d0 100644
--- a/lib/common/nvpair.c
+++ b/lib/common/nvpair.c
@@ -1,967 +1,977 @@
/*
* Copyright 2004-2021 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 <crm_internal.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <glib.h>
#include <libxml/tree.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
#include <crm/common/iso8601_internal.h>
#include "crmcommon_private.h"
/*
* This file isolates handling of three types of name/value pairs:
*
* - pcmk_nvpair_t data type
* - XML attributes (<TAG ... NAME=VALUE ...>)
* - XML nvpair elements (<nvpair id=ID name=NAME value=VALUE>)
*/
// pcmk_nvpair_t handling
/*!
* \internal
* \brief Allocate a new name/value pair
*
* \param[in] name New name (required)
* \param[in] value New value
*
* \return Newly allocated name/value pair
* \note The caller is responsible for freeing the result with
* \c pcmk__free_nvpair().
*/
static pcmk_nvpair_t *
pcmk__new_nvpair(const char *name, const char *value)
{
pcmk_nvpair_t *nvpair = NULL;
CRM_ASSERT(name);
nvpair = calloc(1, sizeof(pcmk_nvpair_t));
CRM_ASSERT(nvpair);
nvpair->name = strdup(name);
nvpair->value = value? strdup(value) : NULL;
return nvpair;
}
/*!
* \internal
* \brief Free a name/value pair
*
* \param[in] nvpair Name/value pair to free
*/
static void
pcmk__free_nvpair(gpointer data)
{
if (data) {
pcmk_nvpair_t *nvpair = data;
free(nvpair->name);
free(nvpair->value);
free(nvpair);
}
}
/*!
* \brief Prepend a name/value pair to a list
*
* \param[in,out] nvpairs List to modify
* \param[in] name New entry's name
* \param[in] value New entry's value
*
* \return New head of list
* \note The caller is responsible for freeing the list with
* \c pcmk_free_nvpairs().
*/
GSList *
pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
{
return g_slist_prepend(nvpairs, pcmk__new_nvpair(name, value));
}
/*!
* \brief Free a list of name/value pairs
*
* \param[in] list List to free
*/
void
pcmk_free_nvpairs(GSList *nvpairs)
{
g_slist_free_full(nvpairs, pcmk__free_nvpair);
}
/*!
* \internal
* \brief Compare two name/value pairs
*
* \param[in] a First name/value pair to compare
* \param[in] b Second name/value pair to compare
*
* \return 0 if a == b, 1 if a > b, -1 if a < b
*/
static gint
pcmk__compare_nvpair(gconstpointer a, gconstpointer b)
{
int rc = 0;
const pcmk_nvpair_t *pair_a = a;
const pcmk_nvpair_t *pair_b = b;
CRM_ASSERT(a != NULL);
CRM_ASSERT(pair_a->name != NULL);
CRM_ASSERT(b != NULL);
CRM_ASSERT(pair_b->name != NULL);
rc = strcmp(pair_a->name, pair_b->name);
if (rc < 0) {
return -1;
} else if (rc > 0) {
return 1;
}
return 0;
}
/*!
* \brief Sort a list of name/value pairs
*
* \param[in,out] list List to sort
*
* \return New head of list
*/
GSList *
pcmk_sort_nvpairs(GSList *list)
{
return g_slist_sort(list, pcmk__compare_nvpair);
}
/*!
* \brief Create a list of name/value pairs from an XML node's attributes
*
* \param[in] XML to parse
*
* \return New list of name/value pairs
* \note It is the caller's responsibility to free the list with
* \c pcmk_free_nvpairs().
*/
GSList *
pcmk_xml_attrs2nvpairs(xmlNode *xml)
{
GSList *result = NULL;
for (xmlAttrPtr iter = pcmk__xe_first_attr(xml); iter != NULL;
iter = iter->next) {
result = pcmk_prepend_nvpair(result,
(const char *) iter->name,
(const char *) pcmk__xml_attr_value(iter));
}
return result;
}
/*!
* \internal
* \brief Add an XML attribute corresponding to a name/value pair
*
* Suitable for glib list iterators, this function adds a NAME=VALUE
* XML attribute based on a given name/value pair.
*
* \param[in] data Name/value pair
* \param[out] user_data XML node to add attributes to
*/
static void
pcmk__nvpair_add_xml_attr(gpointer data, gpointer user_data)
{
pcmk_nvpair_t *pair = data;
xmlNode *parent = user_data;
crm_xml_add(parent, pair->name, pair->value);
}
/*!
* \brief Add XML attributes based on a list of name/value pairs
*
* \param[in] list List of name/value pairs
* \param[in,out] xml XML node to add attributes to
*/
void
pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
{
g_slist_foreach(list, pcmk__nvpair_add_xml_attr, xml);
}
// convenience function for name=value strings
/*!
* \internal
* \brief Extract the name and value from an input string formatted as "name=value".
* If unable to extract them, they are returned as NULL.
*
* \param[in] input The input string, likely from the command line
* \param[out] name Everything before the first '=' in the input string
* \param[out] value Everything after the first '=' in the input string
*
* \return 2 if both name and value could be extracted, 1 if only one could, and
* and error code otherwise
*/
int
pcmk__scan_nvpair(const char *input, char **name, char **value)
{
#ifdef SSCANF_HAS_M
*name = NULL;
*value = NULL;
if (sscanf(input, "%m[^=]=%m[^\n]", name, value) <= 0) {
return -pcmk_err_bad_nvpair;
}
#else
char *sep = NULL;
*name = NULL;
*value = NULL;
sep = strstr(optarg, "=");
if (sep == NULL) {
return -pcmk_err_bad_nvpair;
}
*name = strndup(input, sep-input);
if (*name == NULL) {
return -ENOMEM;
}
/* If the last char in optarg is =, the user gave no
* value for the option. Leave it as NULL.
*/
if (*(sep+1) != '\0') {
*value = strdup(sep+1);
if (*value == NULL) {
return -ENOMEM;
}
}
#endif
if (*name != NULL && *value != NULL) {
return 2;
} else if (*name != NULL || *value != NULL) {
return 1;
} else {
return -pcmk_err_bad_nvpair;
}
}
/*!
* \internal
* \brief Format a name/value pair.
*
* Units can optionally be provided for the value. Note that unlike most
* formatting functions, this one returns the formatted string. It is
* assumed that the most common use of this function will be to build up
* a string to be output as part of other functions.
*
* \note The caller is responsible for freeing the return value after use.
*
* \param[in] name The name of the nvpair.
* \param[in] value The value of the nvpair.
* \param[in] units Optional units for the value, or NULL.
+ *
+ * \return Newly allocated string with name/value pair
*/
char *
-pcmk_format_nvpair(const char *name, const char *value, const char *units) {
+pcmk__format_nvpair(const char *name, const char *value, const char *units)
+{
return crm_strdup_printf("%s=\"%s%s\"", name, value, units ? units : "");
}
/*!
* \internal
* \brief Format a name/time pair.
*
- * See pcmk_format_nvpair() for more details.
+ * See pcmk__format_nvpair() for more details.
*
* \note The caller is responsible for freeing the return value after use.
*
* \param[in] name The name for the time.
* \param[in] epoch_time The time to format.
*/
char *
pcmk_format_named_time(const char *name, time_t epoch_time) {
const char *now_str = pcmk__epoch2str(&epoch_time);
return crm_strdup_printf("%s=\"%s\"", name, now_str ? now_str : "");
}
// XML attribute handling
/*!
* \brief Create an XML attribute with specified name and value
*
* \param[in,out] node XML node to modify
* \param[in] name Attribute name to set
* \param[in] value Attribute value to set
*
* \return New value on success, \c NULL otherwise
* \note This does nothing if node, name, or value are \c NULL or empty.
*/
const char *
crm_xml_add(xmlNode *node, const char *name, const char *value)
{
bool dirty = FALSE;
xmlAttr *attr = NULL;
CRM_CHECK(node != NULL, return NULL);
CRM_CHECK(name != NULL, return NULL);
if (value == NULL) {
return NULL;
}
#if XML_PARANOIA_CHECKS
{
const char *old_value = NULL;
old_value = crm_element_value(node, name);
/* Could be re-setting the same value */
CRM_CHECK(old_value != value,
crm_err("Cannot reset %s with crm_xml_add(%s)", name, value);
return value);
}
#endif
if (pcmk__tracking_xml_changes(node, FALSE)) {
const char *old = crm_element_value(node, name);
if (old == NULL || value == NULL || strcmp(old, value) != 0) {
dirty = TRUE;
}
}
if (dirty && (pcmk__check_acl(node, name, xpf_acl_create) == FALSE)) {
crm_trace("Cannot add %s=%s to %s", name, value, node->name);
return NULL;
}
attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
if (dirty) {
pcmk__mark_xml_attr_dirty(attr);
}
CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
return (char *)attr->children->content;
}
/*!
* \brief Replace an XML attribute with specified name and (possibly NULL) value
*
* \param[in,out] node XML node to modify
* \param[in] name Attribute name to set
* \param[in] value Attribute value to set
*
* \return New value on success, \c NULL otherwise
* \note This does nothing if node or name is \c NULL or empty.
*/
const char *
crm_xml_replace(xmlNode *node, const char *name, const char *value)
{
bool dirty = FALSE;
xmlAttr *attr = NULL;
const char *old_value = NULL;
CRM_CHECK(node != NULL, return NULL);
CRM_CHECK(name != NULL && name[0] != 0, return NULL);
old_value = crm_element_value(node, name);
/* Could be re-setting the same value */
CRM_CHECK(old_value != value, return value);
if (pcmk__check_acl(node, name, xpf_acl_write) == FALSE) {
/* Create a fake object linked to doc->_private instead? */
crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
return NULL;
} else if (old_value && !value) {
xml_remove_prop(node, name);
return NULL;
}
if (pcmk__tracking_xml_changes(node, FALSE)) {
if (!old_value || !value || !strcmp(old_value, value)) {
dirty = TRUE;
}
}
attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
if (dirty) {
pcmk__mark_xml_attr_dirty(attr);
}
CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
return (char *) attr->children->content;
}
/*!
* \brief Create an XML attribute with specified name and integer value
*
* This is like \c crm_xml_add() but taking an integer value.
*
* \param[in,out] node XML node to modify
* \param[in] name Attribute name to set
* \param[in] value Attribute value to set
*
* \return New value as string on success, \c NULL otherwise
* \note This does nothing if node or name are \c NULL or empty.
*/
const char *
crm_xml_add_int(xmlNode *node, const char *name, int value)
{
char *number = crm_itoa(value);
const char *added = crm_xml_add(node, name, number);
free(number);
return added;
}
/*!
* \brief Create an XML attribute with specified name and unsigned value
*
* This is like \c crm_xml_add() but taking a guint value.
*
* \param[in,out] node XML node to modify
* \param[in] name Attribute name to set
* \param[in] ms Attribute value to set
*
* \return New value as string on success, \c NULL otherwise
* \note This does nothing if node or name are \c NULL or empty.
*/
const char *
crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
{
char *number = crm_strdup_printf("%u", ms);
const char *added = crm_xml_add(node, name, number);
free(number);
return added;
}
// Maximum size of null-terminated string representation of 64-bit integer
// -9223372036854775808
#define LLSTRSIZE 21
/*!
* \brief Create an XML attribute with specified name and long long int value
*
* This is like \c crm_xml_add() but taking a long long int value. It is a
* useful equivalent for defined types like time_t, etc.
*
* \param[in,out] xml XML node to modify
* \param[in] name Attribute name to set
* \param[in] value Attribute value to set
*
* \return New value as string on success, \c NULL otherwise
* \note This does nothing if xml or name are \c NULL or empty.
* This does not support greater than 64-bit values.
*/
const char *
crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
{
char s[LLSTRSIZE] = { '\0', };
if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
return NULL;
}
return crm_xml_add(xml, name, s);
}
/*!
* \brief Create XML attributes for seconds and microseconds
*
* This is like \c crm_xml_add() but taking a struct timeval.
*
* \param[in,out] xml XML node to modify
* \param[in] name_sec Name of XML attribute for seconds
* \param[in] name_usec Name of XML attribute for microseconds (or NULL)
* \param[in] value Time value to set
*
* \return New seconds value as string on success, \c NULL otherwise
* \note This does nothing if xml, name_sec, or value is \c NULL.
*/
const char *
crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
const struct timeval *value)
{
const char *added = NULL;
if (xml && name_sec && value) {
added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
if (added && name_usec) {
// Any error is ignored (we successfully added seconds)
crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
}
}
return added;
}
/*!
* \brief Retrieve the value of an XML attribute
*
* \param[in] data XML node to check
* \param[in] name Attribute name to check
*
* \return Value of specified attribute (may be \c NULL)
*/
const char *
crm_element_value(const xmlNode *data, const char *name)
{
xmlAttr *attr = NULL;
if (data == NULL) {
crm_err("Couldn't find %s in NULL", name ? name : "<null>");
CRM_LOG_ASSERT(data != NULL);
return NULL;
} else if (name == NULL) {
crm_err("Couldn't find NULL in %s", crm_element_name(data));
return NULL;
}
/* The first argument to xmlHasProp() has always been const,
* but libxml2 <2.9.2 didn't declare that, so cast it
*/
attr = xmlHasProp((xmlNode *) data, (pcmkXmlStr) name);
if (!attr || !attr->children) {
return NULL;
}
return (const char *) attr->children->content;
}
/*!
* \brief Retrieve the integer value of an XML attribute
*
* This is like \c crm_element_value() but getting the value as an integer.
*
* \param[in] data XML node to check
* \param[in] name Attribute name to check
* \param[in] dest Where to store element value
*
* \return 0 on success, -1 otherwise
*/
int
crm_element_value_int(const xmlNode *data, const char *name, int *dest)
{
const char *value = NULL;
CRM_CHECK(dest != NULL, return -1);
value = crm_element_value(data, name);
if (value) {
errno = 0;
*dest = crm_parse_int(value, NULL);
if (errno == 0) {
return 0;
}
}
return -1;
}
/*!
* \brief Retrieve the long long integer value of an XML attribute
*
* This is like \c crm_element_value() but getting the value as a long long int.
*
* \param[in] data XML node to check
* \param[in] name Attribute name to check
* \param[in] dest Where to store element value
*
* \return 0 on success, -1 otherwise
*/
int
crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
{
const char *value = NULL;
CRM_CHECK(dest != NULL, return -1);
value = crm_element_value(data, name);
if (value) {
errno = 0;
*dest = crm_parse_ll(value, NULL);
if (errno == 0) {
return 0;
}
}
return -1;
}
/*!
* \brief Retrieve the millisecond value of an XML attribute
*
* This is like \c crm_element_value() but returning the value as a guint.
*
* \param[in] data XML node to check
* \param[in] name Attribute name to check
* \param[out] dest Where to store attribute value
*
* \return \c pcmk_ok on success, -1 otherwise
*/
int
crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
{
const char *value = NULL;
long long value_ll;
CRM_CHECK(dest != NULL, return -1);
*dest = 0;
value = crm_element_value(data, name);
if (value == NULL) {
return pcmk_ok;
}
errno = 0;
value_ll = crm_parse_ll(value, NULL);
if ((errno != 0) || (value_ll < 0) || (value_ll > G_MAXUINT)) {
return -1;
}
*dest = (guint) value_ll;
return pcmk_ok;
}
/*!
* \brief Retrieve the seconds-since-epoch value of an XML attribute
*
* This is like \c crm_element_value() but returning the value as a time_t.
*
* \param[in] xml XML node to check
* \param[in] name Attribute name to check
* \param[out] dest Where to store attribute value
*
* \return \c pcmk_ok on success, -1 otherwise
*/
int
crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
{
long long value_ll = 0;
if (crm_element_value_ll(xml, name, &value_ll) < 0) {
return -1;
}
/* Unfortunately, we can't do any bounds checking, since time_t has neither
* standardized bounds nor constants defined for them.
*/
*dest = (time_t) value_ll;
return pcmk_ok;
}
/*!
* \brief Retrieve the value of XML second/microsecond attributes as time
*
* This is like \c crm_element_value() but returning value as a struct timeval.
*
* \param[in] xml XML to parse
* \param[in] name_sec Name of XML attribute for seconds
* \param[in] name_usec Name of XML attribute for microseconds
* \param[out] dest Where to store result
*
* \return \c pcmk_ok on success, -errno on error
* \note Values default to 0 if XML or XML attribute does not exist
*/
int
crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
const char *name_usec, struct timeval *dest)
{
long long value_i = 0;
CRM_CHECK(dest != NULL, return -EINVAL);
dest->tv_sec = 0;
dest->tv_usec = 0;
if (xml == NULL) {
return pcmk_ok;
}
/* Unfortunately, we can't do any bounds checking, since there are no
* constants provided for the bounds of time_t and suseconds_t, and
* calculating them isn't worth the effort. If there are XML values
* beyond the native sizes, there will probably be worse problems anyway.
*/
// Parse seconds
errno = 0;
if (crm_element_value_ll(xml, name_sec, &value_i) < 0) {
return -errno;
}
dest->tv_sec = (time_t) value_i;
// Parse microseconds
if (crm_element_value_ll(xml, name_usec, &value_i) < 0) {
return -errno;
}
dest->tv_usec = (suseconds_t) value_i;
return pcmk_ok;
}
/*!
* \brief Retrieve a copy of the value of an XML attribute
*
* This is like \c crm_element_value() but allocating new memory for the result.
*
* \param[in] data XML node to check
* \param[in] name Attribute name to check
*
* \return Value of specified attribute (may be \c NULL)
* \note The caller is responsible for freeing the result.
*/
char *
crm_element_value_copy(const xmlNode *data, const char *name)
{
char *value_copy = NULL;
const char *value = crm_element_value(data, name);
if (value != NULL) {
value_copy = strdup(value);
}
return value_copy;
}
/*!
* \brief Add hash table entry to XML as (possibly legacy) name/value
*
* Suitable for \c g_hash_table_foreach(), this function takes a hash table key
* and value, with an XML node passed as user data, and adds an XML attribute
* with the specified name and value if it does not already exist. If the key
* name starts with a digit, this will instead add a \<param name=NAME
* value=VALUE/> child to the XML (for legacy compatibility with heartbeat).
*
* \param[in] key Key of hash table entry
* \param[in] value Value of hash table entry
* \param[in] user_data XML node
*/
void
hash2smartfield(gpointer key, gpointer value, gpointer user_data)
{
const char *name = key;
const char *s_value = value;
xmlNode *xml_node = user_data;
if (isdigit(name[0])) {
xmlNode *tmp = create_xml_node(xml_node, XML_TAG_PARAM);
crm_xml_add(tmp, XML_NVPAIR_ATTR_NAME, name);
crm_xml_add(tmp, XML_NVPAIR_ATTR_VALUE, s_value);
} else if (crm_element_value(xml_node, name) == NULL) {
crm_xml_add(xml_node, name, s_value);
crm_trace("dumped: %s=%s", name, s_value);
} else {
crm_trace("duplicate: %s=%s", name, s_value);
}
}
/*!
* \brief Set XML attribute based on hash table entry
*
* Suitable for \c g_hash_table_foreach(), this function takes a hash table key
* and value, with an XML node passed as user data, and adds an XML attribute
* with the specified name and value if it does not already exist.
*
* \param[in] key Key of hash table entry
* \param[in] value Value of hash table entry
* \param[in] user_data XML node
*/
void
hash2field(gpointer key, gpointer value, gpointer user_data)
{
const char *name = key;
const char *s_value = value;
xmlNode *xml_node = user_data;
if (crm_element_value(xml_node, name) == NULL) {
crm_xml_add(xml_node, name, s_value);
} else {
crm_trace("duplicate: %s=%s", name, s_value);
}
}
/*!
* \brief Set XML attribute based on hash table entry, as meta-attribute name
*
* Suitable for \c g_hash_table_foreach(), this function takes a hash table key
* and value, with an XML node passed as user data, and adds an XML attribute
* with the meta-attribute version of the specified name and value if it does
* not already exist and if the name does not appear to be cluster-internal.
*
* \param[in] key Key of hash table entry
* \param[in] value Value of hash table entry
* \param[in] user_data XML node
*/
void
hash2metafield(gpointer key, gpointer value, gpointer user_data)
{
char *crm_name = NULL;
if (key == NULL || value == NULL) {
return;
}
/* Filter out cluster-generated attributes that contain a '#' or ':'
* (like fail-count and last-failure).
*/
for (crm_name = key; *crm_name; ++crm_name) {
if ((*crm_name == '#') || (*crm_name == ':')) {
return;
}
}
crm_name = crm_meta_name(key);
hash2field(crm_name, value, user_data);
free(crm_name);
}
// nvpair handling
/*!
* \brief Create an XML name/value pair
*
* \param[in] parent If not \c NULL, make new XML node a child of this one
* \param[in] id If not \c NULL, use this as ID (otherwise auto-generate)
* \param[in] name Name to use
* \param[in] value Value to use
*
* \return New XML object on success, \c NULL otherwise
*/
xmlNode *
crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
const char *value)
{
xmlNode *nvp;
/* id can be NULL so we auto-generate one, and name can be NULL if this
* will be used to delete a name/value pair by ID, but both can't be NULL
*/
CRM_CHECK(id || name, return NULL);
nvp = create_xml_node(parent, XML_CIB_TAG_NVPAIR);
CRM_CHECK(nvp, return NULL);
if (id) {
crm_xml_add(nvp, XML_ATTR_ID, id);
} else {
const char *parent_id = ID(parent);
crm_xml_set_id(nvp, "%s-%s",
(parent_id? parent_id : XML_CIB_TAG_NVPAIR), name);
}
crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name);
crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value);
return nvp;
}
/*!
* \brief Add XML nvpair element based on hash table entry
*
* Suitable for \c g_hash_table_foreach(), this function takes a hash table key
* and value, with an XML node passed as the user data, and adds an \c nvpair
* XML element with the specified name and value.
*
* \param[in] key Key of hash table entry
* \param[in] value Value of hash table entry
* \param[in] user_data XML node
*/
void
hash2nvpair(gpointer key, gpointer value, gpointer user_data)
{
const char *name = key;
const char *s_value = value;
xmlNode *xml_node = user_data;
crm_create_nvpair_xml(xml_node, name, name, s_value);
crm_trace("dumped: name=%s value=%s", name, s_value);
}
/*!
* \brief Retrieve XML attributes as a hash table
*
* Given an XML element, this will look for any \<attributes> element child,
* creating a hash table of (newly allocated string) name/value pairs taken
* first from the attributes element's NAME=VALUE XML attributes, and then
* from any \<param name=NAME value=VALUE> children of attributes.
*
* \param[in] XML node to parse
*
* \return Hash table with name/value pairs
* \note It is the caller's responsibility to free the result using
* \c g_hash_table_destroy().
*/
GHashTable *
xml2list(xmlNode *parent)
{
xmlNode *child = NULL;
xmlAttrPtr pIter = NULL;
xmlNode *nvpair_list = NULL;
GHashTable *nvpair_hash = crm_str_table_new();
CRM_CHECK(parent != NULL, return nvpair_hash);
nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
if (nvpair_list == NULL) {
crm_trace("No attributes in %s", crm_element_name(parent));
crm_log_xml_trace(parent, "No attributes for resource op");
}
crm_log_xml_trace(nvpair_list, "Unpacking");
for (pIter = pcmk__xe_first_attr(nvpair_list); pIter != NULL;
pIter = pIter->next) {
const char *p_name = (const char *)pIter->name;
const char *p_value = pcmk__xml_attr_value(pIter);
crm_trace("Added %s=%s", p_name, p_value);
g_hash_table_insert(nvpair_hash, strdup(p_name), strdup(p_value));
}
for (child = pcmk__xml_first_child(nvpair_list); child != NULL;
child = pcmk__xml_next(child)) {
if (strcmp((const char *)child->name, XML_TAG_PARAM) == 0) {
const char *key = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
const char *value = crm_element_value(child, XML_NVPAIR_ATTR_VALUE);
crm_trace("Added %s=%s", key, value);
if (key != NULL && value != NULL) {
g_hash_table_insert(nvpair_hash, strdup(key), strdup(value));
}
}
}
return nvpair_hash;
}
// Deprecated functions kept only for backward API compatibility
#include <crm/common/util_compat.h>
int
pcmk_scan_nvpair(const char *input, char **name, char **value)
{
return pcmk__scan_nvpair(input, name, value);
}
+char *
+pcmk_format_nvpair(const char *name, const char *value,
+ const char *units)
+{
+ return pcmk__format_nvpair(name, value, units);
+}
+
// End deprecated API
diff --git a/lib/pengine/pe_output.c b/lib/pengine/pe_output.c
index cee8f74608..0d564bc9d9 100644
--- a/lib/pengine/pe_output.c
+++ b/lib/pengine/pe_output.c
@@ -1,2045 +1,2045 @@
/*
* Copyright 2019-2021 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 <crm_internal.h>
#include <crm/common/iso8601_internal.h>
#include <crm/common/xml_internal.h>
#include <crm/msg_xml.h>
#include <crm/pengine/internal.h>
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 && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
- char *pair = pcmk_format_nvpair("interval", interval_ms_s, "ms");
+ 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 *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);
}
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");
+ 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");
+ 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 rc=%d (%s)", call, task,
interval_str ? interval_str : "",
last_change_str ? last_change_str : "",
exec_str ? exec_str : "",
queue_str ? queue_str : "",
rc, services_ocf_exitcode_str(rc));
if (last_change_str) {
free(last_change_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;
}
PCMK__OUTPUT_ARGS("cluster-summary", "pe_working_set_t *", "gboolean", "gboolean", "gboolean",
"gboolean", "gboolean", "gboolean")
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) {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
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;
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
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);
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
out->message(out, "cluster-times", last_written, user, client, origin);
}
if (show_counts) {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
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) {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
out->message(out, "cluster-options", data_set);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
if (out->message(out, "maint-mode", data_set->flags) == pcmk_rc_ok) {
rc = pcmk_rc_ok;
}
return rc;
}
PCMK__OUTPUT_ARGS("cluster-summary", "pe_working_set_t *", "gboolean", "gboolean", "gboolean",
"gboolean", "gboolean", "gboolean")
static int
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) {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
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;
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
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);
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
out->message(out, "cluster-times", last_written, user, client, origin);
}
if (show_counts) {
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
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.
*/
PCMK__OUTPUT_LIST_FOOTER(out, rc);
out->begin_list(out, NULL, NULL, "Config Options");
out->message(out, "cluster-options", data_set);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
if (out->message(out, "maint-mode", data_set->flags) == pcmk_rc_ok) {
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 && !pcmk__str_eq(node->details->uname, node->details->id, pcmk__str_casei)) {
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) {
crm_xml_add(xml_node, param_name, param_value);
}
};
va_end(args);
if (is_list) {
pcmk__output_xml_push_parent(out, xml_node);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "gboolean")
static int
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;
}
PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "gboolean")
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;
}
PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "gboolean")
static int
ban_xml(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 G_GNUC_UNUSED = va_arg(args, gboolean);
char *weight_s = crm_itoa(pe_node->weight);
pcmk__output_create_xml_node(out, "ban",
"id", location->id,
"resource", location->rsc_lh->id,
"node", pe_node->details->uname,
"weight", weight_s,
"master_only", pcmk__btoa(location->role_filter == RSC_ROLE_MASTER),
NULL);
free(weight_s);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
static int
cluster_counts_html(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);
xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li", NULL);
xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li", NULL);
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;
}
PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
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;
}
PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
static int
cluster_counts_xml(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);
xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "nodes_configured", NULL);
xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "resources_configured", NULL);
char *s = crm_itoa(nnodes);
crm_xml_add(nodes_node, "number", s);
free(s);
s = crm_itoa(nresources);
crm_xml_add(resources_node, "number", s);
free(s);
s = crm_itoa(ndisabled);
crm_xml_add(resources_node, "disabled", s);
free(s);
s = crm_itoa(nblocked);
crm_xml_add(resources_node, "blocked", s);
free(s);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
static int
cluster_dc_html(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 *);
xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
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;
}
PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
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;
}
PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
static int
cluster_dc_xml(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 G_GNUC_UNUSED = va_arg(args, char *);
if (dc) {
pcmk__output_create_xml_node(out, "current_dc",
"present", "true",
"version", dc_version_s ? dc_version_s : "",
"name", dc->details->uname,
"id", dc->details->id,
"with_quorum", pcmk__btoa(crm_is_true(quorum)),
NULL);
} else {
pcmk__output_create_xml_node(out, "current_dc",
"present", "false",
NULL);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("maint-mode", "unsigned long long int")
int
pe__cluster_maint_mode_text(pcmk__output_t *out, va_list args) {
unsigned long long flags = va_arg(args, unsigned long long);
if (pcmk_is_set(flags, pe_flag_maintenance_mode)) {
pcmk__formatted_printf(out, "\n *** Resource management is DISABLED ***\n");
pcmk__formatted_printf(out, " The cluster will not attempt to start, stop or recover services\n");
return pcmk_rc_ok;
} else if (pcmk_is_set(flags, pe_flag_stop_everything)) {
pcmk__formatted_printf(out, "\n *** Resource management is DISABLED ***\n");
pcmk__formatted_printf(out, " The cluster will keep all resources stopped\n");
return pcmk_rc_ok;
} else {
return pcmk_rc_no_output;
}
}
PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
static int
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",
pcmk_is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled");
out->list_item(out, NULL, "Cluster is %s",
pcmk_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_demote:
out->list_item(out, NULL, "No quorum policy: Demote promotable "
"resources and stop all other 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 (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
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 if (pcmk_is_set(data_set->flags, pe_flag_stop_everything)) {
xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: ");
pcmk_create_html_node(node, "span", NULL, "bold", "STOPPED");
pcmk_create_html_node(node, "span", NULL, NULL,
" (the cluster will keep all resources stopped)");
} else {
out->list_item(out, NULL, "Resource management: enabled");
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
static int
cluster_options_log(pcmk__output_t *out, va_list args) {
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
if (pcmk_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 if (pcmk_is_set(data_set->flags, pe_flag_stop_everything)) {
out->info(out, "Resource management is DISABLED. The cluster has stopped all resources.");
return pcmk_rc_ok;
} else {
return pcmk_rc_no_output;
}
}
PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
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",
pcmk_is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled");
out->list_item(out, NULL, "Cluster is %s",
pcmk_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_demote:
out->list_item(out, NULL, "No quorum policy: Demote promotable "
"resources and stop all other 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;
}
PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
static int
cluster_options_xml(pcmk__output_t *out, va_list args) {
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
const char *no_quorum_policy = NULL;
switch (data_set->no_quorum_policy) {
case no_quorum_freeze:
no_quorum_policy = "freeze";
break;
case no_quorum_stop:
no_quorum_policy = "stop";
break;
case no_quorum_demote:
no_quorum_policy = "demote";
break;
case no_quorum_ignore:
no_quorum_policy = "ignore";
break;
case no_quorum_suicide:
no_quorum_policy = "suicide";
break;
}
pcmk__output_create_xml_node(out, "cluster_options",
"stonith-enabled", pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)),
"symmetric-cluster", pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_symmetric_cluster)),
"no-quorum-policy", no_quorum_policy,
"maintenance-mode", pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)),
"stop-all-resources", pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_stop_everything)),
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
static int
cluster_stack_html(pcmk__output_t *out, va_list args) {
const char *stack_s = va_arg(args, const char *);
xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
pcmk_create_html_node(node, "span", NULL, "bold", "Stack: ");
pcmk_create_html_node(node, "span", NULL, NULL, stack_s);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
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;
}
PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
static int
cluster_stack_xml(pcmk__output_t *out, va_list args) {
const char *stack_s = va_arg(args, const char *);
pcmk__output_create_xml_node(out, "stack",
"type", stack_s,
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
static int
cluster_times_html(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 *);
xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li", NULL);
xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li", NULL);
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;
}
PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
static int
cluster_times_xml(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 *);
pcmk__output_create_xml_node(out, "last_update",
"time", pcmk__epoch2str(NULL),
NULL);
pcmk__output_create_xml_node(out, "last_change",
"time", last_written ? last_written : "",
"user", user ? user : "",
"client", client ? client : "",
"origin", origin ? origin : "",
NULL);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
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;
}
PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr")
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;
}
PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr")
static int
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",
op_key ? "op_key" : "id", op_key ? op_key : ID(xml_op),
"node", crm_element_value(xml_op, XML_ATTR_UNAME),
"exitstatus", services_ocf_exitcode_str(rc),
"exitreason", reason_s,
"exitcode", rc_s,
"call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
"status", services_lrm_status_str(status),
NULL);
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);
pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE, rc_change,
"queued", crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
"exec", crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
"interval", s,
"task", crm_element_value(xml_op, XML_LRM_ATTR_TASK),
NULL);
free(s);
free(rc_change);
crm_time_free(crm_when);
}
free(reason_s);
free(rc_s);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
"gboolean", "gboolean", "gboolean", "GList *", "GList *")
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);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
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", NULL);
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) {
GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
if (rscs != NULL) {
out->begin_list(out, NULL, NULL, NULL);
pe__rscs_brief_output(out, rscs, print_opts | pe_print_rsconly, FALSE);
out->end_list(out);
}
} else if (group_by_node) {
GList *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_node, only_rsc);
}
out->end_list(out);
}
} else {
out->begin_list(out, NULL, NULL, "%s", buf);
}
free(buf);
free(node_name);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
"gboolean", "gboolean", "gboolean", "GList *", "GList *")
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);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
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) {
if (print_brief) {
GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
if (rscs != NULL) {
out->begin_list(out, NULL, NULL, "%s", buf);
out->begin_list(out, NULL, NULL, "Resources");
pe__rscs_brief_output(out, rscs, print_opts | pe_print_rsconly, FALSE);
out->end_list(out);
out->end_list(out);
}
} else {
GList *gIter2 = NULL;
out->begin_list(out, NULL, NULL, "%s", buf);
out->begin_list(out, NULL, NULL, "Resources");
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_node, only_rsc);
}
out->end_list(out);
out->end_list(out);
}
} else {
out->list_item(out, NULL, "%s", buf);
}
free(buf);
free(node_name);
} else {
char *node_name = pe__node_display_name(node, print_clone_detail);
out->begin_list(out, NULL, NULL, "Node: %s", node_name);
free(node_name);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
"gboolean", "gboolean", "gboolean", "GList *", "GList *")
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);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
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", pcmk__btoa(node->details->online),
"standby", pcmk__btoa(node->details->standby),
"standby_onfail", pcmk__btoa(node->details->standby_onfail),
"maintenance", pcmk__btoa(node->details->maintenance),
"pending", pcmk__btoa(node->details->pending),
"unclean", pcmk__btoa(node->details->unclean),
"shutdown", pcmk__btoa(node->details->shutdown),
"expected_up", pcmk__btoa(node->details->expected_up),
"is_dc", pcmk__btoa(node->details->is_dc),
"resources_running", length_s,
"type", node_type);
if (pe__is_guest_node(node)) {
xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out);
crm_xml_add(xml_node, "id_as_resource", node->details->remote_rsc->container->id);
}
if (group_by_node) {
GList *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_node, only_rsc);
}
}
free(length_s);
out->end_list(out);
} else {
pcmk__output_xml_create_parent(out, "node",
"name", node->details->uname,
NULL);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
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;
}
PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
static int
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", NULL);
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;
}
PCMK__OUTPUT_ARGS("node-and-op", "pe_working_set_t *", "xmlNodePtr")
static int
node_and_op(pcmk__output_t *out, va_list args) {
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
pe_resource_t *rsc = NULL;
gchar *node_str = NULL;
char *last_change_str = NULL;
const char *op_rsc = crm_element_value(xml_op, "resource");
const char *status_s = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS);
const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
int status = crm_parse_int(status_s, "0");
time_t last_change = 0;
rsc = pe_find_resource(data_set->resources, op_rsc);
if (rsc) {
pe_node_t *node = pe__current_node(rsc);
const char *target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
int opts = pe_print_rsconly | pe_print_pending;
if (node == NULL) {
node = rsc->pending_node;
}
node_str = pcmk__native_output_string(rsc, rsc_printable_id(rsc), node,
opts, target_role, false);
} else {
node_str = crm_strdup_printf("Unknown resource %s", op_rsc);
}
if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
&last_change) == pcmk_ok) {
last_change_str = crm_strdup_printf(", %s=%s, exec=%sms",
XML_RSC_OP_LAST_CHANGE,
crm_strip_trailing_newline(ctime(&last_change)),
crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
}
out->list_item(out, NULL, "%s: %s (node=%s, call=%s, rc=%s%s): %s",
node_str, op_key ? op_key : ID(xml_op),
crm_element_value(xml_op, XML_ATTR_UNAME),
crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
crm_element_value(xml_op, XML_LRM_ATTR_RC),
last_change_str ? last_change_str : "",
services_lrm_status_str(status));
g_free(node_str);
free(last_change_str);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("node-and-op", "pe_working_set_t *", "xmlNodePtr")
static int
node_and_op_xml(pcmk__output_t *out, va_list args) {
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
pe_resource_t *rsc = NULL;
const char *op_rsc = crm_element_value(xml_op, "resource");
const char *status_s = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS);
const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
int status = crm_parse_int(status_s, "0");
time_t last_change = 0;
xmlNode *node = pcmk__output_create_xml_node(out, "operation",
"op", op_key ? op_key : ID(xml_op),
"node", crm_element_value(xml_op, XML_ATTR_UNAME),
"call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
"rc", crm_element_value(xml_op, XML_LRM_ATTR_RC),
"status", services_lrm_status_str(status),
NULL);
rsc = pe_find_resource(data_set->resources, op_rsc);
if (rsc) {
const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE);
char *agent_tuple = NULL;
agent_tuple = crm_strdup_printf("%s:%s:%s", class,
pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider) ? crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER) : "",
kind);
pcmk__xe_set_props(node, "rsc", rsc_printable_id(rsc),
"agent", agent_tuple,
NULL);
free(agent_tuple);
}
if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
&last_change) == pcmk_ok) {
pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE, crm_strip_trailing_newline(ctime(&last_change)),
XML_RSC_OP_T_EXEC, crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
NULL);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
static int
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",
"name", name,
"value", value,
NULL);
if (add_extra) {
char *buf = crm_itoa(expected_score);
crm_xml_add(node, "expected", buf);
free(buf);
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "unsigned int", "gboolean", "gboolean", "gboolean")
static int
node_list_html(pcmk__output_t *out, va_list args) {
GList *nodes = va_arg(args, GList *);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
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);
int rc = pcmk_rc_no_output;
for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
pe_node_t *node = (pe_node_t *) gIter->data;
if (!pcmk__str_in_list(only_node, node->details->uname)) {
continue;
}
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Node List");
out->message(out, "node", node, print_opts, TRUE, NULL, print_clone_detail,
print_brief, group_by_node, only_node, only_rsc);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "unsigned int", "gboolean", "gboolean", "gboolean")
int
pe__node_list_text(pcmk__output_t *out, va_list args) {
GList *nodes = va_arg(args, GList *);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
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;
size_t online_nodes_len = 0;
size_t online_remote_nodes_len = 0;
size_t online_guest_nodes_len = 0;
size_t offline_nodes_len = 0;
size_t offline_remote_nodes_len = 0;
int rc = pcmk_rc_no_output;
for (GList *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_node, node->details->uname)) {
free(node_name);
continue;
}
PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Node List");
/* 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)) {
pcmk__add_word(&online_guest_nodes,
&online_guest_nodes_len, node_name);
} else if (pe__is_remote_node(node)) {
pcmk__add_word(&online_remote_nodes,
&online_remote_nodes_len, node_name);
} else {
pcmk__add_word(&online_nodes, &online_nodes_len, node_name);
}
free(node_name);
continue;
}
} else {
node_mode = "OFFLINE";
if (group_by_node == FALSE) {
if (pe__is_remote_node(node)) {
pcmk__add_word(&offline_remote_nodes,
&offline_remote_nodes_len, node_name);
} else if (pe__is_guest_node(node)) {
/* ignore offline guest nodes */
} else {
pcmk__add_word(&offline_nodes,
&offline_nodes_len, 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_node, only_rsc);
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);
}
PCMK__OUTPUT_LIST_FOOTER(out, rc);
return rc;
}
PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "unsigned int", "gboolean", "gboolean", "gboolean")
static int
node_list_xml(pcmk__output_t *out, va_list args) {
GList *nodes = va_arg(args, GList *);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
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 (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
pe_node_t *node = (pe_node_t *) gIter->data;
if (!pcmk__str_in_list(only_node, node->details->uname)) {
continue;
}
out->message(out, "node", node, print_opts, TRUE, NULL, print_clone_detail,
print_brief, group_by_node, only_node, only_rsc);
}
out->end_list(out);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int", "gboolean")
int
pe__op_history_text(pcmk__output_t *out, va_list args) {
xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
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;
}
PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int", "gboolean")
static int
op_history_xml(pcmk__output_t *out, va_list args) {
xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
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 = crm_itoa(rc);
xmlNodePtr node = pcmk__output_create_xml_node(out, "operation_history",
"call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
"task", task,
"rc", rc_s,
"rc_text", services_ocf_exitcode_str(rc),
NULL);
free(rc_s);
if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
char *s = crm_strdup_printf("%sms", interval_ms_s);
crm_xml_add(node, "interval", 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) {
crm_xml_add(node, XML_RSC_OP_LAST_CHANGE, pcmk__epoch2str(&int_value));
}
}
value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
if (value) {
char *s = crm_strdup_printf("%sms", value);
crm_xml_add(node, XML_RSC_OP_T_EXEC, s);
free(s);
}
value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
if (value) {
char *s = crm_strdup_printf("%sms", value);
crm_xml_add(node, XML_RSC_OP_T_QUEUE, s);
free(s);
}
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("resource-config", "pe_resource_t *", "gboolean")
static int
resource_config(pcmk__output_t *out, va_list args) {
pe_resource_t *rsc = va_arg(args, pe_resource_t *);
gboolean raw = va_arg(args, gboolean);
char *rsc_xml = NULL;
if (raw) {
rsc_xml = dump_xml_formatted(rsc->orig_xml ? rsc->orig_xml : rsc->xml);
} else {
rsc_xml = dump_xml_formatted(rsc->xml);
}
pcmk__formatted_printf(out, "Resource XML:\n");
out->output_xml(out, "xml", rsc_xml);
free(rsc_xml);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("resource-history", "pe_resource_t *", "const char *", "gboolean", "int", "time_t", "gboolean")
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;
}
PCMK__OUTPUT_ARGS("resource-history", "pe_resource_t *", "const char *", "gboolean", "int", "time_t", "gboolean")
static int
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",
"id", rsc_id,
NULL);
if (rsc == NULL) {
crm_xml_add(node, "orphan", "true");
} else if (all || failcount || last_failure > 0) {
char *migration_s = crm_itoa(rsc->migration_threshold);
pcmk__xe_set_props(node, "orphan", "false",
"migration-threshold", migration_s,
NULL);
free(migration_s);
if (failcount > 0) {
char *s = crm_itoa(failcount);
crm_xml_add(node, PCMK__FAIL_COUNT_PREFIX, s);
free(s);
}
if (last_failure > 0) {
crm_xml_add(node, PCMK__LAST_FAILURE_PREFIX, pcmk__epoch2str(&last_failure));
}
}
if (as_header == FALSE) {
pcmk__output_xml_pop_parent(out);
}
return pcmk_rc_ok;
}
static void
print_resource_header(pcmk__output_t *out, gboolean group_by_node,
gboolean inactive_resources)
{
if (group_by_node) {
/* Active resources have already been printed by node */
out->begin_list(out, NULL, NULL, "Inactive Resources");
} else if (inactive_resources) {
out->begin_list(out, NULL, NULL, "Full List of Resources");
} else {
out->begin_list(out, NULL, NULL, "Active Resources");
}
}
PCMK__OUTPUT_ARGS("resource-list", "pe_working_set_t *", "unsigned int", "gboolean",
"gboolean", "gboolean", "gboolean", "GList *", "GList *", "gboolean")
static int
resource_list(pcmk__output_t *out, va_list args)
{
pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
unsigned int print_opts = va_arg(args, unsigned int);
gboolean group_by_node = va_arg(args, gboolean);
gboolean inactive_resources = va_arg(args, gboolean);
gboolean brief_output = va_arg(args, gboolean);
gboolean print_summary = va_arg(args, gboolean);
GList *only_node = va_arg(args, GList *);
GList *only_rsc = va_arg(args, GList *);
gboolean print_spacer = va_arg(args, gboolean);
GList *rsc_iter;
int rc = pcmk_rc_no_output;
bool printed_header = false;
/* If we already showed active resources by node, and
* we're not showing inactive resources, we have nothing to do
*/
if (group_by_node && !inactive_resources) {
return rc;
}
/* If we haven't already printed resources grouped by node,
* and brief output was requested, print resource summary */
if (brief_output && !group_by_node) {
GList *rscs = pe__filter_rsc_list(data_set->resources, only_rsc);
PCMK__OUTPUT_SPACER_IF(out, print_spacer);
print_resource_header(out, group_by_node, inactive_resources);
printed_header = true;
pe__rscs_brief_output(out, rscs, print_opts, inactive_resources);
g_list_free(rscs);
}
/* 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;
int x;
/* 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 (pcmk_is_set(rsc->flags, pe_rsc_orphan) && !is_active) {
continue;
/* Skip active resources if we already displayed them by node */
} else if (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 && !inactive_resources) {
continue;
} else if (partially_active && !pe__rsc_running_on_any_node_in_list(rsc, only_node)) {
continue;
}
if (!printed_header) {
PCMK__OUTPUT_SPACER_IF(out, print_spacer);
print_resource_header(out, group_by_node, inactive_resources);
printed_header = true;
}
/* Print this resource */
x = out->message(out, crm_map_element_name(rsc->xml), print_opts, rsc,
only_node, only_rsc);
if (x == pcmk_rc_ok) {
rc = pcmk_rc_ok;
}
}
if (print_summary && rc != pcmk_rc_ok) {
if (!printed_header) {
PCMK__OUTPUT_SPACER_IF(out, print_spacer);
print_resource_header(out, group_by_node, inactive_resources);
printed_header = true;
}
if (group_by_node) {
out->list_item(out, NULL, "No inactive resources");
} else if (inactive_resources) {
out->list_item(out, NULL, "No resources");
} else {
out->list_item(out, NULL, "No active resources");
}
}
if (printed_header) {
out->end_list(out);
}
return rc;
}
PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
static int
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;
}
PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
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, "%s%s %s",
ticket->granted ? "granted" : "revoked",
ticket->standby ? " [standby]" : "",
time);
free(time);
} else {
out->list_item(out, ticket->id, "%s%s",
ticket->granted ? "granted" : "revoked",
ticket->standby ? " [standby]" : "");
}
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
static int
ticket_xml(pcmk__output_t *out, va_list args) {
pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
xmlNodePtr node = NULL;
node = pcmk__output_create_xml_node(out, "ticket",
"id", ticket->id,
"status", ticket->granted ? "granted" : "revoked",
"standby", pcmk__btoa(ticket->standby),
NULL);
if (ticket->last_granted > -1) {
crm_xml_add(node, "last-granted", pcmk__epoch2str(&ticket->last_granted));
}
return pcmk_rc_ok;
}
static pcmk__message_entry_t fmt_functions[] = {
{ "ban", "html", ban_html },
{ "ban", "log", pe__ban_text },
{ "ban", "text", pe__ban_text },
{ "ban", "xml", 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", cluster_counts_html },
{ "cluster-counts", "log", pe__cluster_counts_text },
{ "cluster-counts", "text", pe__cluster_counts_text },
{ "cluster-counts", "xml", cluster_counts_xml },
{ "cluster-dc", "html", cluster_dc_html },
{ "cluster-dc", "log", pe__cluster_dc_text },
{ "cluster-dc", "text", pe__cluster_dc_text },
{ "cluster-dc", "xml", cluster_dc_xml },
{ "cluster-options", "html", cluster_options_html },
{ "cluster-options", "log", cluster_options_log },
{ "cluster-options", "text", pe__cluster_options_text },
{ "cluster-options", "xml", cluster_options_xml },
{ "cluster-summary", "default", pe__cluster_summary },
{ "cluster-summary", "html", cluster_summary_html },
{ "cluster-stack", "html", cluster_stack_html },
{ "cluster-stack", "log", pe__cluster_stack_text },
{ "cluster-stack", "text", pe__cluster_stack_text },
{ "cluster-stack", "xml", cluster_stack_xml },
{ "cluster-times", "html", cluster_times_html },
{ "cluster-times", "log", pe__cluster_times_text },
{ "cluster-times", "text", pe__cluster_times_text },
{ "cluster-times", "xml", cluster_times_xml },
{ "failed-action", "default", pe__failed_action_text },
{ "failed-action", "xml", 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", "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-and-op", "default", node_and_op },
{ "node-and-op", "xml", node_and_op_xml },
{ "node-list", "html", node_list_html },
{ "node-list", "log", pe__node_list_text },
{ "node-list", "text", pe__node_list_text },
{ "node-list", "xml", node_list_xml },
{ "node-attribute", "html", node_attribute_html },
{ "node-attribute", "log", pe__node_attribute_text },
{ "node-attribute", "text", pe__node_attribute_text },
{ "node-attribute", "xml", node_attribute_xml },
{ "op-history", "default", pe__op_history_text },
{ "op-history", "xml", op_history_xml },
{ "primitive", "xml", pe__resource_xml },
{ "primitive", "html", pe__resource_html },
{ "primitive", "text", pe__resource_text },
{ "primitive", "log", pe__resource_text },
{ "resource-config", "default", resource_config },
{ "resource-history", "default", pe__resource_history_text },
{ "resource-history", "xml", resource_history_xml },
{ "resource-list", "default", resource_list },
{ "ticket", "html", ticket_html },
{ "ticket", "log", pe__ticket_text },
{ "ticket", "text", pe__ticket_text },
{ "ticket", "xml", 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("<NULL>");
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");
GList *gIter = node->details->running_rsc;
GList *all = NULL;
all = g_list_prepend(all, 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, all, all);
}
g_list_free_full(all, free);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jun 26, 6:25 PM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1959409
Default Alt Text
(127 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment