Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/pacemaker.h b/include/pacemaker.h
index 0f641f4753..a6f04ca0ff 100644
--- a/include/pacemaker.h
+++ b/include/pacemaker.h
@@ -1,706 +1,704 @@
/*
* Copyright 2019-2024 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/common/scheduler.h>
# include <crm/cib/cib_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
* controller 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
* controller 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
* controller 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
* controller 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 Remove a resource
*
* \param[in,out] xml Destination for the result, as an XML tree
* \param[in] rsc_id Resource to remove
* \param[in] rsc_type Type of the resource ("primitive", "group", etc.)
*
* \return Standard Pacemaker return code
* \note This function will return \p pcmk_rc_ok if \p rsc_id doesn't exist
* or if \p rsc_type is incorrect for \p rsc_id (deleting something
* that doesn't exist always succeeds).
*/
int pcmk_resource_delete(xmlNodePtr *xml, const char *rsc_id, const char *rsc_type);
/*!
* \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
*
* \return Standard Pacemaker return code
*/
int pcmk_resource_digests(xmlNodePtr *xml, pcmk_resource_t *rsc,
const pcmk_node_t *node, GHashTable *overrides);
/*!
* \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] scheduler Scheduler data
* \param[in] injections 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 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)
*
* \return Standard Pacemaker return code
*/
int pcmk_simulate(xmlNodePtr *xml, pcmk_scheduler_t *scheduler,
const pcmk_injections_t *injections, unsigned int flags,
unsigned int section_opts, const char *use_date,
const char *input_file, const char *graph_file,
const char *dot_file);
/*!
* \brief Verify that a CIB is error-free or output errors and warnings
*
* This high-level function essentially implements crm_verify(8). It operates
* on an input CIB file, which can be inputted through one of several ways. It
* writes out XML-formatted output.
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] cib_source Source of the CIB:
* NULL -> use live cib, "-" -> stdin
* "<..." -> xml str, otherwise -> xml file name
*
* \return Standard Pacemaker return code
*/
int pcmk_verify(xmlNodePtr *xml, const char *cib_source);
/*!
* \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, const char *node_types);
/*!
* \brief Output cluster status formatted like `crm_mon --output-as=xml`
*
* \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);
/*!
* \brief List available providers for the given OCF agent
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] agent_spec Resource agent name
*
* \return Standard Pacemaker return code
*/
int pcmk_list_alternatives(xmlNodePtr *xml, const char *agent_spec);
/*!
* \brief List all agents available for the named standard and/or provider
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] agent_spec STD[:PROV]
*
* \return Standard Pacemaker return code
*/
int pcmk_list_agents(xmlNodePtr *xml, char *agent_spec);
/*!
* \brief List all available OCF providers for the given agent
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] agent_spec Resource agent name
*
* \return Standard Pacemaker return code
*/
int pcmk_list_providers(xmlNodePtr *xml, const char *agent_spec);
/*!
* \brief List all available resource agent standards
*
* \param[in,out] xml The destination for the result, as an XML tree
*
* \return Standard Pacemaker return code
*/
int pcmk_list_standards(xmlNodePtr *xml);
/*!
* \brief List all available cluster options
*
* These are options that affect the entire cluster.
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] all If \c true, include advanced and deprecated options
* (currently always treated as true)
*
* \return Standard Pacemaker return code
*/
int pcmk_list_cluster_options(xmlNode **xml, bool all);
/*!
* \brief List common fencing resource parameters
*
* These are parameters that are available for all fencing resources, regardless
* of type. They are processed by Pacemaker, rather than by the fence agent or
* the fencing library.
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] all If \c true, include advanced and deprecated options
* (currently always treated as true)
*
* \return Standard Pacemaker return code
*/
int pcmk_list_fencing_params(xmlNode **xml, bool all);
/*!
* \internal
* \brief List meta-attributes applicable to primitive resources as OCF-like XML
*
* \param[in,out] out Output object
* \param[in] all If \c true, include advanced and deprecated options (this
* is always treated as true for XML output objects)
*
* \return Standard Pacemaker return code
*/
int pcmk_list_primitive_meta(xmlNode **xml, bool all);
/*!
* \brief Return constraints that apply to the given ticket
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] ticket_id Ticket to find constraint for, or \c NULL for
* all ticket constraints
*
* \return Standard Pacemaker return code
*/
int pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id);
/*!
* \brief Delete a ticket's state from the local cluster site
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] ticket_id Ticket to delete
* \param[in] force If \c true, delete the ticket even if it has
* been granted
*
* \return Standard Pacemaker return code
*/
int pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force);
/*!
* \brief Return the value of a ticket's attribute
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] ticket_id Ticket to find attribute value for
* \param[in] attr_name Attribute's name to find value for
* \param[in] attr_default If either the ticket or the attribute do not
* exist, use this as the value in \p xml
*
* \return Standard Pacemaker return code
*/
int pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id,
const char *attr_name, const char *attr_default);
/*!
* \brief Return information about the given ticket
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] ticket_id Ticket to find info value for, or \c NULL for
* all tickets
*
* \return Standard Pacemaker return code
*/
int pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id);
/*!
* \brief Remove the given attribute(s) from a ticket
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] ticket_id Ticket to remove attributes from
* \param[in] attr_delete A list of attribute names
* \param[in] force Attempting to remove the granted attribute of
* \p ticket_id will cause this function to return
* \c EACCES unless \p force is set to \c true
*
* \return Standard Pacemaker return code
*/
int pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete,
bool force);
/*!
* \brief Set the given attribute(s) on a ticket
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] ticket_id Ticket to set attributes on
* \param[in] attr_set A hash table of attributes, where keys are the
* attribute names and the values are the attribute
* values
* \param[in] force Attempting to change the granted status of
* \p ticket_id will cause this function to return
* \c EACCES unless \p force is set to \c true
*
* \return Standard Pacemaker return code
*
* \note If no \p ticket_id attribute exists but \p attr_set is non-NULL, the
* ticket will be created with the given attributes.
*/
int pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set,
bool force);
/*!
* \brief Return a ticket's state XML
*
* \param[in,out] xml The destination for the result, as an XML tree
* \param[in] ticket_id Ticket to find state for, or \c NULL for all
* tickets
*
* \return Standard Pacemaker return code
*
* \note If \p ticket_id is not \c NULL and more than one ticket exists with
* that ID, this function returns \c pcmk_rc_duplicate_id.
*/
int pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id);
/*!
* \brief Ask the cluster to perform fencing
*
* \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 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
* fencing action (-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(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] target The node to get history for
* \param[in] timeout How long to wait for 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, const char *target, unsigned int timeout,
bool quiet, int verbose, bool broadcast, bool cleanup);
/*!
* \brief List all installed fence agents
*
* \param[in,out] xml The destination for the result, as an XML tree (if
* not NULL, previous contents will be freed and lost)
* \param[in] timeout How long to wait for operation to complete (in ms)
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_installed(xmlNodePtr *xml, unsigned int timeout);
/*!
* \brief When was a device last fenced?
*
* \param[in,out] xml The destination for the result, as an XML tree (if
* not NULL, previous contents will be freed and lost)
* \param[in] target The node that was fenced
* \param[in] as_nodeid If true, \p target has node ID rather than name
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid);
/*!
* \brief List nodes that can be fenced
*
* \param[in,out] xml The destination for the result, as an XML tree (if
* not NULL, previous contents will be freed and lost)
* \param[in] device_id Resource ID of fence device to check
* \param[in] timeout How long to wait for operation to complete (in ms)
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_list_targets(xmlNodePtr *xml, const char *device_id,
unsigned int timeout);
/*!
* \brief Get metadata for a fence agent
*
* \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 (if
* not NULL, previous contents will be freed and lost)
- * \param[in,out] st A connection to the fencer API
* \param[in] agent The fence agent to get metadata for
* \param[in] timeout How long to wait for operation to complete (in ms)
*
* \return Standard Pacemaker return code
*/
-int pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, const char *agent,
- unsigned int timeout);
+int pcmk_fence_metadata(xmlNodePtr *xml, const char *agent, unsigned int timeout);
/*!
* \brief List registered fence devices
*
* \param[in,out] xml The destination for the result, as an XML tree (if
* not NULL, previous contents will be freed and lost)
* \param[in,out] st A connection to the fencer API
* \param[in] target If not NULL, return only devices that can fence this
* \param[in] timeout How long to wait for operation to complete (in ms)
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, const char *target,
unsigned int timeout);
/*!
* \brief Register a fencing topology level
*
* \param[in,out] st A connection to the fencer API
* \param[in] target What fencing level targets (as "name=value" to
* target by given node attribute, or "@pattern" to
* target by node name pattern, or a node name)
* \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, const char *target,
int fence_level,
const stonith_key_value_t *devices);
/*!
* \brief Unregister a fencing topology level
*
* \param[in,out] st A connection to the fencer API
* \param[in] target What fencing level targets (as "name=value" to
* target by given node attribute, or "@pattern" to
* target by node name pattern, or a node name)
* \param[in] fence_level Index number of level to remove
*
* \return Standard Pacemaker return code
*/
int pcmk_fence_unregister_level(stonith_t *st, const char *target,
int fence_level);
/*!
* \brief Validate a fence device configuration
*
* \param[in,out] xml The destination for the result, as an XML tree (if
* not NULL, previous contents will be freed and lost)
* \param[in,out] st A connection to the fencer API
* \param[in] agent The agent to validate (for example, "fence_xvm")
* \param[in] id Fence device ID (may be NULL)
* \param[in] params Fence device configuration parameters
* \param[in] timeout How long to wait for 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, const stonith_key_value_t *params,
unsigned int timeout);
#ifdef __cplusplus
}
#endif
#endif
diff --git a/lib/pacemaker/pcmk_fence.c b/lib/pacemaker/pcmk_fence.c
index 95e6401212..4756c18318 100644
--- a/lib/pacemaker/pcmk_fence.c
+++ b/lib/pacemaker/pcmk_fence.c
@@ -1,658 +1,659 @@
/*
* Copyright 2009-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <crm/common/mainloop.h>
#include <crm/common/results.h>
#include <crm/common/output.h>
#include <crm/common/output_internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h> // stonith__*
#include <glib.h>
#include <libxml/tree.h>
#include <pacemaker.h>
#include <pacemaker-internal.h>
#include "libpacemaker_private.h"
static const int st_opts = st_opt_sync_call|st_opt_allow_self_fencing;
static GMainLoop *mainloop = NULL;
static struct {
stonith_t *st;
const char *target;
const char *action;
char *name;
unsigned int timeout;
unsigned int tolerance;
int delay;
pcmk__action_result_t result;
} async_fence_data = { NULL, };
static int
handle_level(stonith_t *st, const char *target, int fence_level,
const stonith_key_value_t *devices, bool added)
{
const char *node = NULL;
const char *pattern = NULL;
const char *name = NULL;
char *value = NULL;
int rc = pcmk_rc_ok;
if (target == NULL) {
// Not really possible, but makes static analysis happy
return EINVAL;
}
/* Determine if targeting by attribute, node name pattern or node name */
value = strchr(target, '=');
if (value != NULL) {
name = target;
*value++ = '\0';
} else if (*target == '@') {
pattern = target + 1;
} else {
node = target;
}
/* Register or unregister level as appropriate */
if (added) {
rc = st->cmds->register_level_full(st, st_opts, node, pattern,
name, value, fence_level,
devices);
} else {
rc = st->cmds->remove_level_full(st, st_opts, node, pattern,
name, value, fence_level);
}
return pcmk_legacy2rc(rc);
}
static stonith_history_t *
reduce_fence_history(stonith_history_t *history)
{
stonith_history_t *new, *hp, *np;
if (!history) {
return history;
}
new = history;
hp = new->next;
new->next = NULL;
while (hp) {
stonith_history_t *hp_next = hp->next;
hp->next = NULL;
for (np = new; ; np = np->next) {
if ((hp->state == st_done) || (hp->state == st_failed)) {
/* action not in progress */
if (pcmk__str_eq(hp->target, np->target, pcmk__str_casei)
&& pcmk__str_eq(hp->action, np->action, pcmk__str_none)
&& (hp->state == np->state)
&& ((hp->state == st_done)
|| pcmk__str_eq(hp->delegate, np->delegate,
pcmk__str_casei))) {
/* purge older hp */
stonith_history_free(hp);
break;
}
}
if (!np->next) {
np->next = hp;
break;
}
}
hp = hp_next;
}
return new;
}
static void
notify_callback(stonith_t * st, stonith_event_t * e)
{
if (pcmk__str_eq(async_fence_data.target, e->target, pcmk__str_casei)
&& pcmk__str_eq(async_fence_data.action, e->action, pcmk__str_none)) {
pcmk__set_result(&async_fence_data.result,
stonith__event_exit_status(e),
stonith__event_execution_status(e),
stonith__event_exit_reason(e));
g_main_loop_quit(mainloop);
}
}
static void
fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
{
pcmk__set_result(&async_fence_data.result, stonith__exit_status(data),
stonith__execution_status(data),
stonith__exit_reason(data));
g_main_loop_quit(mainloop);
}
static gboolean
async_fence_helper(gpointer user_data)
{
stonith_t *st = async_fence_data.st;
int call_id = 0;
int rc = stonith_api_connect_retry(st, async_fence_data.name, 10);
int timeout = 0;
if (rc != pcmk_ok) {
g_main_loop_quit(mainloop);
pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
PCMK_EXEC_NOT_CONNECTED, pcmk_strerror(rc));
return TRUE;
}
st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_FENCE,
notify_callback);
call_id = st->cmds->fence_with_delay(st,
st_opt_allow_self_fencing,
async_fence_data.target,
async_fence_data.action,
async_fence_data.timeout/1000,
async_fence_data.tolerance/1000,
async_fence_data.delay);
if (call_id < 0) {
g_main_loop_quit(mainloop);
pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR,
PCMK_EXEC_ERROR, pcmk_strerror(call_id));
return TRUE;
}
timeout = async_fence_data.timeout / 1000;
if (async_fence_data.delay > 0) {
timeout += async_fence_data.delay;
}
st->cmds->register_callback(st, call_id, timeout, st_opt_timeout_updates,
NULL, "callback", fence_callback);
return TRUE;
}
static int
setup_fencing(stonith_t **st)
{
int rc = pcmk_rc_ok;
*st = stonith_api_new();
if (*st == NULL) {
return ENOMEM;
}
rc = (*st)->cmds->connect(*st, crm_system_name, NULL);
if (rc < 0) {
rc = pcmk_legacy2rc(rc);
stonith_api_delete(*st);
*st = NULL;
}
return rc;
}
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)
{
crm_trigger_t *trig;
int rc = pcmk_rc_ok;
async_fence_data.st = st;
async_fence_data.name = strdup(name);
async_fence_data.target = target;
async_fence_data.action = action;
async_fence_data.timeout = timeout;
async_fence_data.tolerance = tolerance;
async_fence_data.delay = delay;
pcmk__set_result(&async_fence_data.result, CRM_EX_ERROR, PCMK_EXEC_UNKNOWN,
NULL);
trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL);
mainloop_set_trigger(trig);
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
free(async_fence_data.name);
if (reason != NULL) {
// Give the caller ownership of the exit reason
*reason = async_fence_data.result.exit_reason;
async_fence_data.result.exit_reason = NULL;
}
rc = stonith__result2rc(&async_fence_data.result);
pcmk__reset_result(&async_fence_data.result);
return rc;
}
int
pcmk_request_fencing(const char *target, const char *action, const char *name,
unsigned int timeout, unsigned int tolerance, int delay,
char **reason)
{
stonith_t *st = NULL;
int rc = pcmk_rc_ok;
rc = setup_fencing(&st);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__request_fencing(st, target, action, name, timeout, tolerance,
delay, reason);
st->cmds->disconnect(st);
stonith_api_delete(st);
return rc;
}
int
pcmk__fence_history(pcmk__output_t *out, stonith_t *st, const char *target,
unsigned int timeout, int verbose, bool broadcast,
bool cleanup)
{
stonith_history_t *history = NULL;
stonith_history_t *latest = NULL;
int rc = pcmk_rc_ok;
int opts = 0;
if (cleanup) {
out->info(out, "cleaning up fencing-history%s%s",
target ? " for node " : "", target ? target : "");
}
if (broadcast) {
out->info(out, "gather fencing-history from all nodes");
}
stonith__set_call_options(opts, target, st_opts);
if (cleanup) {
stonith__set_call_options(opts, target, st_opt_cleanup);
}
if (broadcast) {
stonith__set_call_options(opts, target, st_opt_broadcast);
}
if (pcmk__str_eq(target, "*", pcmk__str_none)) {
target = NULL;
}
rc = st->cmds->history(st, opts, target, &history, (timeout / 1000));
if (cleanup) {
// Cleanup doesn't return a history list
stonith_history_free(history);
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "event", "events", "Fencing history");
history = stonith__sort_history(history);
for (stonith_history_t *hp = history; hp != NULL; hp = hp->next) {
if (hp->state == st_done) {
latest = hp;
}
if (out->is_quiet(out) || !verbose) {
continue;
}
out->message(out, "stonith-event", hp, true, false,
stonith__later_succeeded(hp, history),
(uint32_t) pcmk_show_failed_detail);
out->increment_list(out);
}
if (latest) {
if (out->is_quiet(out)) {
out->message(out, "stonith-event", latest, false, true, NULL,
(uint32_t) pcmk_show_failed_detail);
} else if (!verbose) { // already printed if verbose
out->message(out, "stonith-event", latest, false, false, NULL,
(uint32_t) pcmk_show_failed_detail);
out->increment_list(out);
}
}
out->end_list(out);
stonith_history_free(history);
return pcmk_legacy2rc(rc);
}
int
pcmk_fence_history(xmlNodePtr *xml, const char *target, unsigned int timeout,
bool quiet, int verbose, bool broadcast, bool cleanup)
{
stonith_t *st = NULL;
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__setup_output_fencing(&out, &st, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
out->quiet = quiet;
rc = pcmk__fence_history(out, st, target, timeout, verbose, broadcast,
cleanup);
pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
st->cmds->disconnect(st);
stonith_api_delete(st);
return rc;
}
int
pcmk__fence_installed(pcmk__output_t *out, stonith_t *st, unsigned int timeout)
{
stonith_key_value_t *devices = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices,
(timeout / 1000));
// rc is a negative error code or a positive number of agents
if (rc < 0) {
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "fence device", "fence devices",
"Installed fence devices");
for (stonith_key_value_t *iter = devices; iter != NULL; iter = iter->next) {
out->list_item(out, "device", "%s", iter->value);
}
out->end_list(out);
stonith_key_value_freeall(devices, 1, 1);
return pcmk_rc_ok;
}
int
pcmk_fence_installed(xmlNodePtr *xml, unsigned int timeout)
{
stonith_t *st = NULL;
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__setup_output_fencing(&out, &st, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__fence_installed(out, st, timeout);
pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
st->cmds->disconnect(st);
stonith_api_delete(st);
return rc;
}
int
pcmk__fence_last(pcmk__output_t *out, const char *target, bool as_nodeid)
{
time_t when = 0;
if (target == NULL) {
return pcmk_rc_ok;
}
if (as_nodeid) {
when = stonith_api_time(atol(target), NULL, FALSE);
} else {
when = stonith_api_time(0, target, FALSE);
}
return out->message(out, "last-fenced", target, when);
}
int
pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
stonith__register_messages(out);
rc = pcmk__fence_last(out, target, as_nodeid);
pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
return rc;
}
int
pcmk__fence_list_targets(pcmk__output_t *out, stonith_t *st,
const char *device_id, unsigned int timeout)
{
GList *targets = NULL;
char *lists = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->list(st, st_opts, device_id, &lists, timeout/1000);
if (rc != pcmk_rc_ok) {
return pcmk_legacy2rc(rc);
}
targets = stonith__parse_targets(lists);
out->begin_list(out, "fence target", "fence targets", "Fence Targets");
while (targets != NULL) {
out->list_item(out, NULL, "%s", (const char *) targets->data);
targets = targets->next;
}
out->end_list(out);
free(lists);
return rc;
}
int
pcmk_fence_list_targets(xmlNodePtr *xml, const char *device_id, unsigned int timeout)
{
stonith_t *st = NULL;
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__setup_output_fencing(&out, &st, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__fence_list_targets(out, st, device_id, timeout);
pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
st->cmds->disconnect(st);
stonith_api_delete(st);
return rc;
}
int
pcmk__fence_metadata(pcmk__output_t *out, stonith_t *st, const char *agent,
unsigned int timeout)
{
char *buffer = NULL;
int rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer,
timeout/1000);
if (rc != pcmk_rc_ok) {
return pcmk_legacy2rc(rc);
}
out->output_xml(out, PCMK_XE_METADATA, buffer);
free(buffer);
return rc;
}
int
-pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, const char *agent,
- unsigned int timeout)
+pcmk_fence_metadata(xmlNodePtr *xml, const char *agent, unsigned int timeout)
{
+ stonith_t *st = NULL;
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
- rc = pcmk__xml_output_new(&out, xml);
+ rc = pcmk__setup_output_fencing(&out, &st, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
- stonith__register_messages(out);
-
rc = pcmk__fence_metadata(out, st, agent, timeout);
pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
+
+ st->cmds->disconnect(st);
+ stonith_api_delete(st);
return rc;
}
int
pcmk__fence_registered(pcmk__output_t *out, stonith_t *st, const char *target,
unsigned int timeout)
{
stonith_key_value_t *devices = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->query(st, st_opts, target, &devices, timeout/1000);
/* query returns a negative error code or a positive number of results. */
if (rc < 0) {
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "fence device", "fence devices",
"Registered fence devices");
for (stonith_key_value_t *iter = devices; iter != NULL; iter = iter->next) {
out->list_item(out, "device", "%s", iter->value);
}
out->end_list(out);
stonith_key_value_freeall(devices, 1, 1);
/* Return pcmk_rc_ok here, not the number of results. Callers probably
* don't care.
*/
return pcmk_rc_ok;
}
int
pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, const char *target,
unsigned int timeout)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
stonith__register_messages(out);
rc = pcmk__fence_registered(out, st, target, timeout);
pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
return rc;
}
int
pcmk__fence_register_level(stonith_t *st, const char *target, int fence_level,
const stonith_key_value_t *devices)
{
return handle_level(st, target, fence_level, devices, true);
}
int
pcmk_fence_register_level(stonith_t *st, const char *target, int fence_level,
const stonith_key_value_t *devices)
{
return pcmk__fence_register_level(st, target, fence_level, devices);
}
int
pcmk__fence_unregister_level(stonith_t *st, const char *target, int fence_level)
{
return handle_level(st, target, fence_level, NULL, false);
}
int
pcmk_fence_unregister_level(stonith_t *st, const char *target, int fence_level)
{
return pcmk__fence_unregister_level(st, target, fence_level);
}
int
pcmk__fence_validate(pcmk__output_t *out, stonith_t *st, const char *agent,
const char *id, const stonith_key_value_t *params,
unsigned int timeout)
{
char *output = NULL;
char *error_output = NULL;
int rc;
rc = st->cmds->validate(st, st_opt_sync_call, id, NULL, agent, params,
timeout/1000, &output, &error_output);
out->message(out, "validate", agent, id, output, error_output, rc);
return pcmk_legacy2rc(rc);
}
int
pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent,
const char *id, const stonith_key_value_t *params,
unsigned int timeout)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__xml_output_new(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
stonith__register_messages(out);
rc = pcmk__fence_validate(out, st, agent, id, params, timeout);
pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
return rc;
}
int
pcmk__get_fencing_history(stonith_t *st, stonith_history_t **stonith_history,
enum pcmk__fence_history fence_history)
{
int rc = pcmk_rc_ok;
if ((st == NULL) || (st->state == stonith_disconnected)) {
rc = ENOTCONN;
} else if (fence_history != pcmk__fence_history_none) {
rc = st->cmds->history(st, st_opt_sync_call, NULL, stonith_history,
120);
rc = pcmk_legacy2rc(rc);
if (rc != pcmk_rc_ok) {
return rc;
}
*stonith_history = stonith__sort_history(*stonith_history);
if (fence_history == pcmk__fence_history_reduced) {
*stonith_history = reduce_fence_history(*stonith_history);
}
}
return rc;
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Oct 29, 8:38 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
942600
Default Alt Text
(47 KB)

Event Timeline