Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/pacemaker.h b/include/pacemaker.h
index be93db790a..d1063a7c2b 100644
--- a/include/pacemaker.h
+++ b/include/pacemaker.h
@@ -1,525 +1,525 @@
/*
* Copyright 2019-2023 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__PACEMAKER__H
# define PCMK__PACEMAKER__H
# include <glib.h>
# include <libxml/tree.h>
# include <crm/cib/cib_types.h>
# include <crm/pengine/pe_types.h>
# include <crm/stonith-ng.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief High Level API
* \ingroup pacemaker
*/
/*!
* \brief Modify operation of running a cluster simulation.
*/
enum pcmk_sim_flags {
pcmk_sim_none = 0,
pcmk_sim_all_actions = 1 << 0,
pcmk_sim_show_pending = 1 << 1,
pcmk_sim_process = 1 << 2,
pcmk_sim_show_scores = 1 << 3,
pcmk_sim_show_utilization = 1 << 4,
pcmk_sim_simulate = 1 << 5,
pcmk_sim_sanitized = 1 << 6,
pcmk_sim_verbose = 1 << 7,
};
/*!
* \brief Synthetic cluster events that can be injected into the cluster
* for running simulations.
*/
typedef struct {
/*! A list of node names (gchar *) to simulate bringing online */
GList *node_up;
/*! A list of node names (gchar *) to simulate bringing offline */
GList *node_down;
/*! A list of node names (gchar *) to simulate failing */
GList *node_fail;
/*! A list of operations (gchar *) to inject. The format of these strings
* is described in the "Operation Specification" section of crm_simulate
* help output.
*/
GList *op_inject;
/*! A list of operations (gchar *) that should return a given error code
* if they fail. The format of these strings is described in the
* "Operation Specification" section of crm_simulate help output.
*/
GList *op_fail;
/*! A list of tickets (gchar *) to simulate granting */
GList *ticket_grant;
/*! A list of tickets (gchar *) to simulate revoking */
GList *ticket_revoke;
/*! A list of tickets (gchar *) to simulate putting on standby */
GList *ticket_standby;
/*! A list of tickets (gchar *) to simulate activating */
GList *ticket_activate;
/*! Does the cluster have an active watchdog device? */
char *watchdog;
/*! Does the cluster have quorum? */
char *quorum;
} pcmk_injections_t;
/*!
* \brief Get and output controller status
*
* \param[in,out] xml Destination for the result, as an XML tree
* \param[in] node_name Name of node whose status is desired
* (\p NULL for DC)
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*/
int pcmk_controller_status(xmlNodePtr *xml, const char *node_name,
unsigned int message_timeout_ms);
/*!
* \brief Get and output designated controller node name
*
* \param[in,out] xml Destination for the result, as an XML tree
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*/
int pcmk_designated_controller(xmlNodePtr *xml,
unsigned int message_timeout_ms);
/*!
* \brief Free a :pcmk_injections_t structure
*
* \param[in,out] injections The structure to be freed
*/
void pcmk_free_injections(pcmk_injections_t *injections);
/*!
* \brief Get and optionally output node info corresponding to a node ID from
* the controller
*
* \param[in,out] xml Destination for the result, as an XML tree
* \param[in,out] node_id ID of node whose name to get. If \p NULL
* or 0, get the local node name. If not
* \p NULL, store the true node ID here on
* success.
* \param[out] node_name If not \p NULL, where to store the node
* name
* \param[out] uuid If not \p NULL, where to store the node
* UUID
* \param[out] state If not \p NULL, where to store the
* membership state
* \param[out] is_remote If not \p NULL, where to store whether the
* node is a Pacemaker Remote node
* \param[out] have_quorum If not \p NULL, where to store whether the
* node has quorum
* \param[in] show_output Whether to output the node info
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*
* \note The caller is responsible for freeing \p *node_name, \p *uuid, and
* \p *state using \p free().
*/
int pcmk_query_node_info(xmlNodePtr *xml, uint32_t *node_id, char **node_name,
char **uuid, char **state, bool *have_quorum,
bool *is_remote, bool show_output,
unsigned int message_timeout_ms);
/*!
* \brief Get the node name corresponding to a node ID from the controller
*
* \param[in,out] xml Destination for the result, as an XML tree
* \param[in,out] node_id ID of node whose name to get (or 0 for the
* local node)
* \param[out] node_name If not \p NULL, where to store the node
* name
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*
* \note The caller is responsible for freeing \p *node_name using \p free().
*/
static inline int
pcmk_query_node_name(xmlNodePtr *xml, uint32_t node_id, char **node_name,
unsigned int message_timeout_ms)
{
return pcmk_query_node_info(xml, &node_id, node_name, NULL, NULL, NULL,
NULL, false, message_timeout_ms);
}
/*!
* \brief Get and output \p pacemakerd status
*
* \param[in,out] xml Destination for the result, as an XML tree
* \param[in] ipc_name IPC name for request
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemakerd API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*/
int pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
unsigned int message_timeout_ms);
/*!
* \brief Calculate and output resource operation digests
*
* \param[out] xml Where to store XML with result
* \param[in,out] rsc Resource to calculate digests for
* \param[in] node Node whose operation history should be used
* \param[in] overrides Hash table of configuration parameters to override
* \param[in] data_set Cluster working set (with status)
*
* \return Standard Pacemaker return code
*/
int pcmk_resource_digests(xmlNodePtr *xml, pe_resource_t *rsc,
const pe_node_t *node, GHashTable *overrides,
pe_working_set_t *data_set);
/**
* \brief Simulate a cluster's response to events.
*
* This high-level function essentially implements crm_simulate(8). It operates
* on an input CIB file and various lists of events that can be simulated. It
* optionally writes out a variety of artifacts to show the results of the
* simulation. Output can be modified with various flags.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in,out] data_set Working set for the cluster.
* \param[in] events A structure containing cluster events
* (node up/down, tickets, injected operations)
* \param[in] flags A bitfield of :pcmk_sim_flags to modify
* operation of the simulation.
* \param[in] section_opts Which portions of the cluster status output
* should be displayed?
* \param[in] use_date The date to set the cluster's time to
* (may be NULL).
* \param[in] input_file The source CIB file, which may be overwritten by
* this function (may be NULL).
* \param[in] graph_file Where to write the XML-formatted transition graph
* (may be NULL, in which case no file will be
* written).
* \param[in] dot_file Where to write the dot(1) formatted transition
* graph (may be NULL, in which case no file will
* be written). See \p pcmk__write_sim_dotfile().
*
* \return Standard Pacemaker return code
*/
int pcmk_simulate(xmlNodePtr *xml, pe_working_set_t *data_set,
pcmk_injections_t *injections, unsigned int flags,
unsigned int section_opts, char *use_date, char *input_file,
char *graph_file, char *dot_file);
/*!
* \brief Get nodes list
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] node_types Node type(s) to return (default: all)
*
* \return Standard Pacemaker return code
*/
-int pcmk_list_nodes(xmlNodePtr *xml, char *node_types);
+int pcmk_list_nodes(xmlNodePtr *xml, const char *node_types);
/*!
* \brief Output the current status of the cluster, formatted in the same way
* that `crm_mon --output-as=xml` would.
*
* \param[in,out] xml The destination for the result, as an XML tree.
*
* \return Standard Pacemaker return code
*/
int pcmk_status(xmlNodePtr *xml);
/*!
* \brief Check whether each rule in a list is in effect
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] input The CIB XML to check (if \c NULL, use current CIB)
* \param[in] date Check whether the rule is in effect at this date and
* time (if \c NULL, use current date and time)
* \param[in] rule_ids The IDs of the rules to check, as a <tt>NULL</tt>-
* terminated list.
*
* \return Standard Pacemaker return code
*/
int pcmk_check_rules(xmlNodePtr *xml, xmlNodePtr input, const crm_time_t *date,
const char **rule_ids);
/*!
* \brief Check whether a given rule is in effect
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] input The CIB XML to check (if \c NULL, use current CIB)
* \param[in] date Check whether the rule is in effect at this date and
* time (if \c NULL, use current date and time)
* \param[in] rule_ids The ID of the rule to check
*
* \return Standard Pacemaker return code
*/
static inline int
pcmk_check_rule(xmlNodePtr *xml, xmlNodePtr input, const crm_time_t *date,
const char *rule_id)
{
const char *rule_ids[] = {rule_id, NULL};
return pcmk_check_rules(xml, input, date, rule_ids);
}
/*
* \enum pcmk_rc_disp_flags
* \brief Bit flags to control which fields of result code info are displayed
*/
enum pcmk_rc_disp_flags {
pcmk_rc_disp_none = 0, //!< (Does nothing)
pcmk_rc_disp_code = (1 << 0), //!< Display result code number
pcmk_rc_disp_name = (1 << 1), //!< Display result code name
pcmk_rc_disp_desc = (1 << 2), //!< Display result code description
};
/*
* \brief Display the name and/or description of a result code
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] code The result code
* \param[in] type Interpret \c code as this type of result code.
* Supported values: \c pcmk_result_legacy,
* \c pcmk_result_rc, \c pcmk_result_exitcode.
* \param[in] flags Group of \c pcmk_rc_disp_flags
*
* \return Standard Pacemaker return code
*/
int pcmk_show_result_code(xmlNodePtr *xml, int code, enum pcmk_result_type type,
uint32_t flags);
/*!
* \brief List all valid result codes in a particular family
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] type The family of result codes to list. Supported
* values: \c pcmk_result_legacy, \c pcmk_result_rc,
* \c pcmk_result_exitcode.
* \param[in] flags Group of \c pcmk_rc_disp_flags
*
* \return Standard Pacemaker return code
*/
int pcmk_list_result_codes(xmlNodePtr *xml, enum pcmk_result_type type,
uint32_t flags);
#ifdef BUILD_PUBLIC_LIBPACEMAKER
/*!
* \brief Ask the cluster to perform fencing
*
* \param[in] st A connection to the fencer API
* \param[in] target The node that should be fenced
* \param[in] action The fencing action (on, off, reboot) to perform
* \param[in] name Who requested the fence action?
* \param[in] timeout How long to wait for the operation to complete (in ms)
* \param[in] tolerance If a successful action for \p target happened within
* this many ms, return 0 without performing the action
* again
* \param[in] delay Apply this delay (in milliseconds) before initiating the
* fencing action (a value of -1 applies no delay and also
* disables any fencing delay from pcmk_delay_base and
* pcmk_delay_max)
* \param[out] reason If not NULL, where to put descriptive failure reason
*
* \return Standard Pacemaker return code
* \note If \p reason is not NULL, the caller is responsible for freeing its
* returned value.
*/
int pcmk_request_fencing(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout,
unsigned int tolerance, int delay, char **reason);
/*!
* \brief List the fencing operations that have occurred for a specific node.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] target The node to get history for.
* \param[in] timeout How long to wait for the operation to complete (in ms).
* \param[in] quiet Suppress most output.
* \param[in] verbose Include additional output.
* \param[in] broadcast Gather fencing history from all nodes.
* \param[in] cleanup Clean up fencing history after listing.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_history(xmlNodePtr *xml, stonith_t *st, char *target,
unsigned int timeout, bool quiet, int verbose,
bool broadcast, bool cleanup);
/*!
* \brief List all installed STONITH agents.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_installed(xmlNodePtr *xml, stonith_t *st, unsigned int timeout);
/*!
* \brief When was a device last fenced?
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] target The node that was fenced.
* \param[in] as_nodeid
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid);
/*!
* \brief List nodes that can be fenced.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] st A connection to the STONITH API
* \param[in] device_id Resource ID of fence device to check
* \param[in] timeout How long to wait for the operation to complete (in ms)
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_list_targets(xmlNodePtr *xml, stonith_t *st,
const char *device_id, unsigned int timeout);
/*!
* \brief Get metadata for a resource.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] agent The fence agent to get metadata for.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, char *agent,
unsigned int timeout);
/*!
* \brief List registered fence devices.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] target If not NULL, only return devices that can fence
* this node.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, char *target,
unsigned int timeout);
/*!
* \brief Register a fencing level for a specific node, node regex, or attribute.
*
* \p target can take three different forms:
* - name=value, in which case \p target is an attribute.
* - @pattern, in which case \p target is a node regex.
* - Otherwise, \p target is a node name.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The object to register a fencing level for.
* \param[in] fence_level Index number of level to add.
* \param[in] devices Devices to use in level.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_register_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices);
/*!
* \brief Unregister a fencing level for a specific node, node regex, or attribute.
*
* \p target can take three different forms:
* - name=value, in which case \p target is an attribute.
* - @pattern, in which case \p target is a node regex.
* - Otherwise, \p target is a node name.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The object to unregister a fencing level for.
* \param[in] fence_level Index number of level to remove.
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_unregister_level(stonith_t *st, char *target, int fence_level);
/*!
* \brief Validate a STONITH device configuration.
*
* \note If \p xml is not NULL, it will be freed first and the previous
* contents lost.
*
* \param[in,out] xml The destination for the result, as an XML tree.
* \param[in] st A connection to the STONITH API.
* \param[in] agent The agent to validate (for example, "fence_xvm").
* \param[in] id STONITH device ID (may be NULL).
* \param[in] params STONITH device configuration parameters.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent,
const char *id, stonith_key_value_t *params,
unsigned int timeout);
#endif
#ifdef __cplusplus
}
#endif
#endif
diff --git a/include/pcmki/pcmki_cluster_queries.h b/include/pcmki/pcmki_cluster_queries.h
index b3d7a18c49..776aa27937 100644
--- a/include/pcmki/pcmki_cluster_queries.h
+++ b/include/pcmki/pcmki_cluster_queries.h
@@ -1,68 +1,69 @@
/*
* Copyright 2020-2022 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__PCMKI_PCMKI_CLUSTER_QUERIES__H
# define PCMK__PCMKI_PCMKI_CLUSTER_QUERIES__H
#include <glib.h> // gboolean, GMainLoop, etc.
#include <crm/crm.h>
#include <crm/common/output_internal.h>
#include <crm/common/ipc_controld.h>
#include <crm/common/ipc_pacemakerd.h>
// CIB queries
-int pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean bash_export);
+int pcmk__list_nodes(pcmk__output_t *out, const char *node_types,
+ gboolean bash_export);
// Controller queries
int pcmk__controller_status(pcmk__output_t *out, const char *node_name,
unsigned int message_timeout_ms);
int pcmk__designated_controller(pcmk__output_t *out,
unsigned int message_timeout_ms);
int pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
unsigned int message_timeout_ms, bool show_output,
enum pcmk_pacemakerd_state *state);
int pcmk__query_node_info(pcmk__output_t *out, uint32_t *node_id,
char **node_name, char **uuid, char **state,
bool *have_quorum, bool *is_remote, bool show_output,
unsigned int message_timeout_ms);
/*!
* \internal
* \brief Get the node name corresponding to a node ID from the controller
*
* \param[in,out] out Output object
* \param[in] node_id ID of node whose name to get (or 0 for
* the local node)
* \param[out] node_name If not \p NULL, where to store the node
* name
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*
* \note The caller is responsible for freeing \p *node_name using \p free().
*/
static inline int
pcmk__query_node_name(pcmk__output_t *out, uint32_t nodeid, char **node_name,
unsigned int message_timeout_ms)
{
return pcmk__query_node_info(out, &nodeid, node_name, NULL, NULL, NULL,
NULL, false, message_timeout_ms);
}
// pacemakerd queries
int pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
unsigned int message_timeout_ms, bool show_output,
enum pcmk_pacemakerd_state *state);
#endif
diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c
index 9a99bfd421..6002cd495f 100644
--- a/lib/pacemaker/pcmk_cluster_queries.c
+++ b/lib/pacemaker/pcmk_cluster_queries.c
@@ -1,899 +1,900 @@
/*
* Copyright 2020-2022 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 <glib.h> // gboolean, GMainLoop, etc.
#include <libxml/tree.h> // xmlNode
#include <pacemaker.h>
#include <pacemaker-internal.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/cib/internal.h>
#include <crm/msg_xml.h>
#include <crm/common/output_internal.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
#include <crm/common/iso8601.h>
#include <crm/common/ipc_controld.h>
#include <crm/common/ipc_pacemakerd.h>
//! Object to store node info from the controller API
typedef struct {
/* Adapted from pcmk_controld_api_reply_t:data:node_info.
* (char **) are convenient here for use within callbacks: we can skip
* copying strings unless the caller passes a non-NULL value.
*/
uint32_t id;
char **node_name;
char **uuid;
char **state;
bool have_quorum;
bool is_remote;
} node_info_t;
//! Object to store API results, a timeout, and an output object
typedef struct {
pcmk__output_t *out;
bool show_output;
int rc;
unsigned int message_timeout_ms;
enum pcmk_pacemakerd_state pcmkd_state;
node_info_t node_info;
} data_t;
/*!
* \internal
* \brief Validate that an IPC API event is a good reply
*
* \param[in,out] data API results and options
* \param[in] api IPC API connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
*
* \return Standard Pacemaker return code
*/
static int
validate_reply_event(data_t *data, const pcmk_ipc_api_t *api,
enum pcmk_ipc_event event_type, crm_exit_t status)
{
pcmk__output_t *out = data->out;
switch (event_type) {
case pcmk_ipc_event_reply:
break;
case pcmk_ipc_event_disconnect:
if (data->rc == ECONNRESET) { // Unexpected
out->err(out, "error: Lost connection to %s",
pcmk_ipc_name(api, true));
}
// Nothing bad but not the reply we're looking for
return ENOTSUP;
default:
// Ditto
return ENOTSUP;
}
if (status != CRM_EX_OK) {
out->err(out, "error: Bad reply from %s: %s",
pcmk_ipc_name(api, true), crm_exit_str(status));
data->rc = EBADMSG;
return data->rc;
}
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Validate that a controller API event is a good reply of expected type
*
* \param[in,out] data API results and options
* \param[in] api Controller connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in] event_data Event-specific data
* \param[in] expected_type Expected reply type
*
* \return Standard Pacemaker return code
*/
static int
validate_controld_reply(data_t *data, const pcmk_ipc_api_t *api,
enum pcmk_ipc_event event_type, crm_exit_t status,
const void *event_data,
enum pcmk_controld_api_reply expected_type)
{
pcmk__output_t *out = data->out;
int rc = pcmk_rc_ok;
const pcmk_controld_api_reply_t *reply = NULL;
rc = validate_reply_event(data, api, event_type, status);
if (rc != pcmk_rc_ok) {
return rc;
}
reply = (const pcmk_controld_api_reply_t *) event_data;
if (reply->reply_type != expected_type) {
out->err(out, "error: Unexpected reply type '%s' from controller",
pcmk__controld_api_reply2str(reply->reply_type));
data->rc = EBADMSG;
return data->rc;
}
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Validate that a \p pacemakerd API event is a good reply of expected
* type
*
* \param[in,out] data API results and options
* \param[in] api \p pacemakerd connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in] event_data Event-specific data
* \param[in] expected_type Expected reply type
*
* \return Standard Pacemaker return code
*/
static int
validate_pcmkd_reply(data_t *data, const pcmk_ipc_api_t *api,
enum pcmk_ipc_event event_type, crm_exit_t status,
const void *event_data,
enum pcmk_pacemakerd_api_reply expected_type)
{
pcmk__output_t *out = data->out;
const pcmk_pacemakerd_api_reply_t *reply = NULL;
int rc = validate_reply_event(data, api, event_type, status);
if (rc != pcmk_rc_ok) {
return rc;
}
reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
if (reply->reply_type != expected_type) {
out->err(out, "error: Unexpected reply type '%s' from pacemakerd",
pcmk__pcmkd_api_reply2str(reply->reply_type));
data->rc = EBADMSG;
return data->rc;
}
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Process a controller status IPC event
*
* \param[in,out] controld_api Controller connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in,out] event_data \p pcmk_controld_api_reply_t object containing
* event-specific data
* \param[in,out] user_data \p data_t object for API results and options
*/
static void
controller_status_event_cb(pcmk_ipc_api_t *controld_api,
enum pcmk_ipc_event event_type, crm_exit_t status,
void *event_data, void *user_data)
{
data_t *data = (data_t *) user_data;
pcmk__output_t *out = data->out;
const pcmk_controld_api_reply_t *reply = NULL;
int rc = validate_controld_reply(data, controld_api, event_type, status,
event_data, pcmk_controld_reply_ping);
if (rc != pcmk_rc_ok) {
return;
}
reply = (const pcmk_controld_api_reply_t *) event_data;
out->message(out, "health",
reply->data.ping.sys_from, reply->host_from,
reply->data.ping.fsa_state, reply->data.ping.result);
data->rc = pcmk_rc_ok;
}
/*!
* \internal
* \brief Process a designated controller IPC event
*
* \param[in,out] controld_api Controller connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in,out] event_data \p pcmk_controld_api_reply_t object containing
* event-specific data
* \param[in,out] user_data \p data_t object for API results and options
*/
static void
designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
enum pcmk_ipc_event event_type,
crm_exit_t status, void *event_data,
void *user_data)
{
data_t *data = (data_t *) user_data;
pcmk__output_t *out = data->out;
const pcmk_controld_api_reply_t *reply = NULL;
int rc = validate_controld_reply(data, controld_api, event_type, status,
event_data, pcmk_controld_reply_ping);
if (rc != pcmk_rc_ok) {
return;
}
reply = (const pcmk_controld_api_reply_t *) event_data;
out->message(out, "dc", reply->host_from);
data->rc = pcmk_rc_ok;
}
/*!
* \internal
* \brief Process a node info IPC event
*
* \param[in,out] controld_api Controller connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in,out] event_data \p pcmk_controld_api_reply_t object containing
* event-specific data
* \param[in,out] user_data \p data_t object for API results and options
*/
static void
node_info_event_cb(pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type,
crm_exit_t status, void *event_data, void *user_data)
{
data_t *data = (data_t *) user_data;
pcmk__output_t *out = data->out;
const pcmk_controld_api_reply_t *reply = NULL;
int rc = validate_controld_reply(data, controld_api, event_type, status,
event_data, pcmk_controld_reply_info);
if (rc != pcmk_rc_ok) {
return;
}
reply = (const pcmk_controld_api_reply_t *) event_data;
if (reply->data.node_info.uname == NULL) {
out->err(out, "Node is not known to cluster");
data->rc = pcmk_rc_node_unknown;
return;
}
data->node_info.have_quorum = reply->data.node_info.have_quorum;
data->node_info.is_remote = reply->data.node_info.is_remote;
data->node_info.id = (uint32_t) reply->data.node_info.id;
pcmk__str_update(data->node_info.node_name, reply->data.node_info.uname);
pcmk__str_update(data->node_info.uuid, reply->data.node_info.uuid);
pcmk__str_update(data->node_info.state, reply->data.node_info.state);
if (data->show_output) {
out->message(out, "node-info",
reply->data.node_info.id, reply->data.node_info.uname,
reply->data.node_info.uuid, reply->data.node_info.state,
reply->data.node_info.have_quorum,
reply->data.node_info.is_remote);
}
data->rc = pcmk_rc_ok;
}
/*!
* \internal
* \brief Process a \p pacemakerd status IPC event
*
* \param[in,out] pacemakerd_api \p pacemakerd connection
* \param[in] event_type Type of event that occurred
* \param[in] status Event status
* \param[in,out] event_data \p pcmk_pacemakerd_api_reply_t object
* containing event-specific data
* \param[in,out] user_data \p data_t object for API results and options
*/
static void
pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
enum pcmk_ipc_event event_type, crm_exit_t status,
void *event_data, void *user_data)
{
data_t *data = user_data;
pcmk__output_t *out = data->out;
const pcmk_pacemakerd_api_reply_t *reply = NULL;
int rc = validate_pcmkd_reply(data, pacemakerd_api, event_type, status,
event_data, pcmk_pacemakerd_reply_ping);
if (rc != pcmk_rc_ok) {
return;
}
// Parse desired information from reply
reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
data->pcmkd_state = reply->data.ping.state;
data->rc = pcmk_rc_ok;
if (!data->show_output) {
return;
}
if (reply->data.ping.status == pcmk_rc_ok) {
out->message(out, "pacemakerd-health",
reply->data.ping.sys_from, reply->data.ping.state, NULL,
reply->data.ping.last_good);
} else {
out->message(out, "pacemakerd-health",
reply->data.ping.sys_from, reply->data.ping.state,
"query failed", time(NULL));
}
}
static pcmk_ipc_api_t *
ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
{
int rc;
pcmk__output_t *out = data->out;
pcmk_ipc_api_t *api = NULL;
rc = pcmk_new_ipc_api(&api, server);
if (api == NULL) {
out->err(out, "error: Could not connect to %s: %s",
pcmk_ipc_name(api, true),
pcmk_rc_str(rc));
data->rc = rc;
return NULL;
}
if (cb != NULL) {
pcmk_register_ipc_callback(api, cb, data);
}
rc = pcmk_connect_ipc(api, dispatch_type);
if (rc != pcmk_rc_ok) {
if (rc == EREMOTEIO) {
data->pcmkd_state = pcmk_pacemakerd_state_remote;
if (eremoteio_ok) {
/* EREMOTEIO may be expected and acceptable for some callers
* on a Pacemaker Remote node
*/
rc = pcmk_rc_ok;
} else {
out->err(out, "error: Could not connect to %s: %s",
pcmk_ipc_name(api, true), pcmk_rc_str(rc));
}
}
data->rc = rc;
pcmk_free_ipc_api(api);
return NULL;
}
return api;
}
/*!
* \internal
* \brief Poll an IPC API connection until timeout or a reply is received
*
* \param[in,out] data API results and options
* \param[in,out] api IPC API connection
* \param[in] on_node If not \p NULL, name of the node to poll (used only
* for logging)
*
* \note Sets the \p rc member of \p data on error
*/
static void
poll_until_reply(data_t *data, pcmk_ipc_api_t *api, const char *on_node)
{
pcmk__output_t *out = data->out;
uint64_t start_nsec = qb_util_nano_current_get();
uint64_t end_nsec = start_nsec;
uint64_t elapsed_ms = 0;
uint64_t remaining_ms = data->message_timeout_ms;
while (remaining_ms > 0) {
int rc = pcmk_poll_ipc(api, remaining_ms);
if (rc == EAGAIN) {
// Poll timed out
break;
}
if (rc != pcmk_rc_ok) {
out->err(out, "error: Failed to poll %s API%s%s: %s",
pcmk_ipc_name(api, true), (on_node != NULL)? " on " : "",
pcmk__s(on_node, ""), pcmk_rc_str(rc));
data->rc = rc;
return;
}
pcmk_dispatch_ipc(api);
if (data->rc != EAGAIN) {
// Received a reply
return;
}
end_nsec = qb_util_nano_current_get();
elapsed_ms = (end_nsec - start_nsec) / QB_TIME_NS_IN_MSEC;
remaining_ms = data->message_timeout_ms - elapsed_ms;
}
out->err(out,
"error: Timed out after %ums waiting for reply from %s API%s%s",
data->message_timeout_ms, pcmk_ipc_name(api, true),
(on_node != NULL)? " on " : "", pcmk__s(on_node, ""));
data->rc = EAGAIN;
}
/*!
* \internal
* \brief Get and output controller status
*
* \param[in,out] out Output object
* \param[in] node_name Name of node whose status is desired
* (\p NULL for DC)
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*/
int
pcmk__controller_status(pcmk__output_t *out, const char *node_name,
unsigned int message_timeout_ms)
{
data_t data = {
.out = out,
.rc = EAGAIN,
.message_timeout_ms = message_timeout_ms,
};
enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
pcmk_ipc_api_t *controld_api = NULL;
if (message_timeout_ms == 0) {
dispatch_type = pcmk_ipc_dispatch_sync;
}
controld_api = ipc_connect(&data, pcmk_ipc_controld,
controller_status_event_cb, dispatch_type,
false);
if (controld_api != NULL) {
int rc = pcmk_controld_api_ping(controld_api, node_name);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Could not ping controller API on %s: %s",
pcmk__s(node_name, "DC"), pcmk_rc_str(rc));
data.rc = rc;
}
if (dispatch_type == pcmk_ipc_dispatch_poll) {
poll_until_reply(&data, controld_api, pcmk__s(node_name, "DC"));
}
pcmk_free_ipc_api(controld_api);
}
return data.rc;
}
// Documented in header
int
pcmk_controller_status(xmlNodePtr *xml, const char *node_name,
unsigned int message_timeout_ms)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
rc = pcmk__controller_status(out, node_name, message_timeout_ms);
pcmk__xml_output_finish(out, xml);
return rc;
}
/*!
* \internal
* \brief Get and output designated controller node name
*
* \param[in,out] out Output object
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*/
int
pcmk__designated_controller(pcmk__output_t *out,
unsigned int message_timeout_ms)
{
data_t data = {
.out = out,
.rc = EAGAIN,
.message_timeout_ms = message_timeout_ms,
};
enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
pcmk_ipc_api_t *controld_api = NULL;
if (message_timeout_ms == 0) {
dispatch_type = pcmk_ipc_dispatch_sync;
}
controld_api = ipc_connect(&data, pcmk_ipc_controld,
designated_controller_event_cb, dispatch_type,
false);
if (controld_api != NULL) {
int rc = pcmk_controld_api_ping(controld_api, NULL);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Could not ping controller API on DC: %s",
pcmk_rc_str(rc));
data.rc = rc;
}
if (dispatch_type == pcmk_ipc_dispatch_poll) {
poll_until_reply(&data, controld_api, "DC");
}
pcmk_free_ipc_api(controld_api);
}
return data.rc;
}
// Documented in header
int
pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
rc = pcmk__designated_controller(out, message_timeout_ms);
pcmk__xml_output_finish(out, xml);
return rc;
}
/*!
* \internal
* \brief Get and optionally output node info corresponding to a node ID from
* the controller
*
* \param[in,out] out Output object
* \param[in,out] node_id ID of node whose name to get. If \p NULL
* or 0, get the local node name. If not
* \p NULL, store the true node ID here on
* success.
* \param[out] node_name If not \p NULL, where to store the node
* name
* \param[out] uuid If not \p NULL, where to store the node
* UUID
* \param[out] state If not \p NULL, where to store the
* membership state
* \param[out] is_remote If not \p NULL, where to store whether the
* node is a Pacemaker Remote node
* \param[out] have_quorum If not \p NULL, where to store whether the
* node has quorum
* \param[in] show_output Whether to show the node info
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemaker-controld API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
*
* \return Standard Pacemaker return code
*
* \note The caller is responsible for freeing \p *node_name, \p *uuid, and
* \p *state using \p free().
*/
int
pcmk__query_node_info(pcmk__output_t *out, uint32_t *node_id, char **node_name,
char **uuid, char **state, bool *have_quorum,
bool *is_remote, bool show_output,
unsigned int message_timeout_ms)
{
data_t data = {
.out = out,
.show_output = show_output,
.rc = EAGAIN,
.message_timeout_ms = message_timeout_ms,
.node_info = {
.id = (node_id == NULL)? 0 : *node_id,
.node_name = node_name,
.uuid = uuid,
.state = state,
},
};
enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
pcmk_ipc_api_t *controld_api = NULL;
if (node_name != NULL) {
*node_name = NULL;
}
if (uuid != NULL) {
*uuid = NULL;
}
if (state != NULL) {
*state = NULL;
}
if (message_timeout_ms == 0) {
dispatch_type = pcmk_ipc_dispatch_sync;
}
controld_api = ipc_connect(&data, pcmk_ipc_controld, node_info_event_cb,
dispatch_type, false);
if (controld_api != NULL) {
int rc = pcmk_controld_api_node_info(controld_api,
(node_id != NULL)? *node_id : 0);
if (rc != pcmk_rc_ok) {
out->err(out,
"error: Could not send request to controller API on local "
"node: %s", pcmk_rc_str(rc));
data.rc = rc;
}
if (dispatch_type == pcmk_ipc_dispatch_poll) {
poll_until_reply(&data, controld_api, "local node");
}
pcmk_free_ipc_api(controld_api);
}
if (data.rc != pcmk_rc_ok) {
return data.rc;
}
// String outputs are set in callback
if (node_id != NULL) {
*node_id = data.node_info.id;
}
if (have_quorum != NULL) {
*have_quorum = data.node_info.have_quorum;
}
if (is_remote != NULL) {
*is_remote = data.node_info.is_remote;
}
return data.rc;
}
// Documented in header
int
pcmk_query_node_info(xmlNodePtr *xml, uint32_t *node_id, char **node_name,
char **uuid, char **state, bool *have_quorum,
bool *is_remote, bool show_output,
unsigned int message_timeout_ms)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
CRM_ASSERT(node_name != NULL);
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
rc = pcmk__query_node_info(out, node_id, node_name, uuid, state,
have_quorum, is_remote, show_output,
message_timeout_ms);
pcmk__xml_output_finish(out, xml);
return rc;
}
/*!
* \internal
* \brief Get and optionally output \p pacemakerd status
*
* \param[in,out] out Output object
* \param[in] ipc_name IPC name for request
* \param[in] message_timeout_ms How long to wait for a reply from the
* \p pacemakerd API. If 0,
* \p pcmk_ipc_dispatch_sync will be used.
* Otherwise, \p pcmk_ipc_dispatch_poll will
* be used.
* \param[in] show_output Whether to output the \p pacemakerd state
* \param[out] state Where to store the \p pacemakerd state, if
* not \p NULL
*
* \return Standard Pacemaker return code
*
* \note This function sets \p state to \p pcmk_pacemakerd_state_remote and
* returns \p pcmk_rc_ok if the IPC connection attempt returns
* \p EREMOTEIO. That code indicates that this is a Pacemaker Remote node
* with \p pacemaker-remoted running. The node may be connected to the
* cluster.
*/
int
pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
unsigned int message_timeout_ms, bool show_output,
enum pcmk_pacemakerd_state *state)
{
data_t data = {
.out = out,
.show_output = show_output,
.rc = EAGAIN,
.message_timeout_ms = message_timeout_ms,
.pcmkd_state = pcmk_pacemakerd_state_invalid,
};
enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
pcmk_ipc_api_t *pacemakerd_api = NULL;
if (message_timeout_ms == 0) {
dispatch_type = pcmk_ipc_dispatch_sync;
}
pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
pacemakerd_event_cb, dispatch_type, true);
if (pacemakerd_api != NULL) {
int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
if (rc != pcmk_rc_ok) {
out->err(out, "error: Could not ping launcher API: %s",
pcmk_rc_str(rc));
data.rc = rc;
}
if (dispatch_type == pcmk_ipc_dispatch_poll) {
poll_until_reply(&data, pacemakerd_api, NULL);
}
pcmk_free_ipc_api(pacemakerd_api);
} else if ((data.pcmkd_state == pcmk_pacemakerd_state_remote)
&& show_output) {
// No API connection so the callback wasn't run
out->message(out, "pacemakerd-health",
NULL, data.pcmkd_state, NULL, time(NULL));
}
if (state != NULL) {
*state = data.pcmkd_state;
}
return data.rc;
}
// Documented in header
int
pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
unsigned int message_timeout_ms)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
rc = pcmk__pacemakerd_status(out, ipc_name, message_timeout_ms, true, NULL);
pcmk__xml_output_finish(out, xml);
return rc;
}
/* user data for looping through remote node xpath searches */
struct node_data {
pcmk__output_t *out;
int found;
const char *field; /* XML attribute to check for node name */
const char *type;
gboolean bash_export;
};
static void
remote_node_print_helper(xmlNode *result, void *user_data)
{
struct node_data *data = user_data;
pcmk__output_t *out = data->out;
const char *name = crm_element_value(result, XML_ATTR_UNAME);
const char *id = crm_element_value(result, data->field);
// node name and node id are the same for remote/guest nodes
out->message(out, "crmadmin-node", data->type,
name ? name : id,
id,
data->bash_export);
data->found++;
}
// \return Standard Pacemaker return code
int
-pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean bash_export)
+pcmk__list_nodes(pcmk__output_t *out, const char *node_types,
+ gboolean bash_export)
{
xmlNode *xml_node = NULL;
int rc;
rc = cib__signon_query(out, NULL, &xml_node);
if (rc == pcmk_rc_ok) {
struct node_data data = {
.out = out,
.found = 0,
.bash_export = bash_export
};
out->begin_list(out, NULL, NULL, "nodes");
if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
node_types = NULL;
}
if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
data.field = "id";
data.type = "cluster";
crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
remote_node_print_helper, &data);
}
if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
data.field = "value";
data.type = "guest";
crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
remote_node_print_helper, &data);
}
if (pcmk__str_empty(node_types) || !pcmk__strcmp(node_types, ",|^remote", pcmk__str_regex)) {
data.field = "id";
data.type = "remote";
crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
remote_node_print_helper, &data);
}
out->end_list(out);
if (data.found == 0) {
out->info(out, "No nodes configured");
}
free_xml(xml_node);
}
return rc;
}
int
-pcmk_list_nodes(xmlNodePtr *xml, char *node_types)
+pcmk_list_nodes(xmlNodePtr *xml, const char *node_types)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
pcmk__register_lib_messages(out);
rc = pcmk__list_nodes(out, node_types, FALSE);
pcmk__xml_output_finish(out, xml);
return rc;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Oct 16, 12:13 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2491360
Default Alt Text
(54 KB)

Event Timeline