diff --git a/include/crm/Makefile.am b/include/crm/Makefile.am index 6dd52fdc19..fcba5a7c3b 100644 --- a/include/crm/Makefile.am +++ b/include/crm/Makefile.am @@ -1,22 +1,29 @@ # -# Copyright 2004-2021 the Pacemaker project contributors +# Copyright 2004-2023 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. # MAINTAINERCLEANFILES = Makefile.in headerdir=$(pkgincludedir)/crm -header_HEADERS = cib.h cluster.h compatibility.h crm.h \ - lrmd.h msg_xml.h services.h stonith-ng.h \ +header_HEADERS = cib.h \ + cluster.h \ + compatibility.h \ + crm.h \ crm_compat.h \ + lrmd.h \ + lrmd_events.h \ + msg_xml.h \ msg_xml_compat.h \ - services_compat.h + services.h \ + services_compat.h \ + stonith-ng.h noinst_HEADERS = lrmd_internal.h services_internal.h SUBDIRS = common pengine cib fencing cluster diff --git a/include/crm/common/util.h b/include/crm/common/util.h index 0959e2f034..d41c5d3bb0 100644 --- a/include/crm/common/util.h +++ b/include/crm/common/util.h @@ -1,152 +1,152 @@ /* * Copyright 2004-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__CRM_COMMON_UTIL__H # define PCMK__CRM_COMMON_UTIL__H # include // gid_t, mode_t, size_t, time_t, uid_t # include # include # include // uint32_t # include # include # include # include -# include +# include // lrmd_event_data_t # include # include # include #ifdef __cplusplus extern "C" { #endif /** * \file * \brief Utility functions * \ingroup core */ # define ONLINESTATUS "online" // Status of an online client # define OFFLINESTATUS "offline" // Status of an offline client /* public node attribute functions (from attrd_client.c) */ char *pcmk_promotion_score_name(const char *rsc_id); /* public Pacemaker Remote functions (from remote.c) */ int crm_default_remote_port(void); /* public score-related functions (from scores.c) */ const char *pcmk_readable_score(int score); int char2score(const char *score); int pcmk__add_scores(int score1, int score2); /* public string functions (from strings.c) */ gboolean crm_is_true(const char *s); int crm_str_to_boolean(const char *s, int *ret); long long crm_get_msec(const char *input); char * crm_strip_trailing_newline(char *str); char *crm_strdup_printf(char const *format, ...) G_GNUC_PRINTF(1, 2); guint crm_parse_interval_spec(const char *input); // Public functions related to resource actions (from actions.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(const 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); bool pcmk_is_probe(const char *task, guint interval); bool pcmk_xe_is_probe(const xmlNode *xml_op); bool pcmk_xe_mask_probe_failure(const xmlNode *xml_op); 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)) 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 #endif #ifdef __cplusplus } #endif #endif diff --git a/include/crm/lrmd.h b/include/crm/lrmd.h index dfc2f25008..0c5a40b62c 100644 --- a/include/crm/lrmd.h +++ b/include/crm/lrmd.h @@ -1,628 +1,561 @@ /* * Copyright 2012-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__CRM_LRMD__H # define PCMK__CRM_LRMD__H #include // bool #include // guint, GList #include +#include #include #ifdef __cplusplus extern "C" { #endif /** * \file * \brief Resource agent executor * \ingroup lrmd */ typedef struct lrmd_s lrmd_t; typedef struct lrmd_key_value_s { char *key; char *value; struct lrmd_key_value_s *next; } lrmd_key_value_t; /* This should be bumped every time there is an incompatible change that * prevents older clients from connecting to this version of the server. */ #define LRMD_PROTOCOL_VERSION "1.1" /* This is the version that the client version will actually be compared * against. This should be identical to LRMD_PROTOCOL_VERSION. However, we * accidentally bumped LRMD_PROTOCOL_VERSION in 6424a647 (1.1.15) when we didn't * need to, so for now it's different. If we ever have a truly incompatible * bump, we can drop this and compare against LRMD_PROTOCOL_VERSION. */ #define LRMD_MIN_PROTOCOL_VERSION "1.0" /* *INDENT-OFF* */ #define DEFAULT_REMOTE_KEY_LOCATION PACEMAKER_CONFIG_DIR "/authkey" #define ALT_REMOTE_KEY_LOCATION "/etc/corosync/authkey" #define DEFAULT_REMOTE_PORT 3121 #define DEFAULT_REMOTE_USERNAME "lrmd" #define F_LRMD_OPERATION "lrmd_op" #define F_LRMD_CLIENTNAME "lrmd_clientname" #define F_LRMD_IS_IPC_PROVIDER "lrmd_is_ipc_provider" #define F_LRMD_CLIENTID "lrmd_clientid" #define F_LRMD_PROTOCOL_VERSION "lrmd_protocol_version" #define F_LRMD_REMOTE_MSG_TYPE "lrmd_remote_msg_type" #define F_LRMD_REMOTE_MSG_ID "lrmd_remote_msg_id" #define F_LRMD_CALLBACK_TOKEN "lrmd_async_id" #define F_LRMD_CALLID "lrmd_callid" #define F_LRMD_CALLOPTS "lrmd_callopt" #define F_LRMD_CALLDATA "lrmd_calldata" #define F_LRMD_RC "lrmd_rc" #define F_LRMD_EXEC_RC "lrmd_exec_rc" #define F_LRMD_OP_STATUS "lrmd_exec_op_status" #define F_LRMD_TIMEOUT "lrmd_timeout" #define F_LRMD_WATCHDOG "lrmd_watchdog" #define F_LRMD_CLASS "lrmd_class" #define F_LRMD_PROVIDER "lrmd_provider" #define F_LRMD_TYPE "lrmd_type" #define F_LRMD_ORIGIN "lrmd_origin" #define F_LRMD_RSC_RUN_TIME "lrmd_run_time" #define F_LRMD_RSC_RCCHANGE_TIME "lrmd_rcchange_time" #define F_LRMD_RSC_EXEC_TIME "lrmd_exec_time" #define F_LRMD_RSC_QUEUE_TIME "lrmd_queue_time" #define F_LRMD_RSC_ID "lrmd_rsc_id" #define F_LRMD_RSC_ACTION "lrmd_rsc_action" #define F_LRMD_RSC_USERDATA_STR "lrmd_rsc_userdata_str" #define F_LRMD_RSC_OUTPUT "lrmd_rsc_output" #define F_LRMD_RSC_EXIT_REASON "lrmd_rsc_exit_reason" #define F_LRMD_RSC_START_DELAY "lrmd_rsc_start_delay" #define F_LRMD_RSC_INTERVAL "lrmd_rsc_interval" #define F_LRMD_RSC_DELETED "lrmd_rsc_deleted" #define F_LRMD_RSC "lrmd_rsc" #define F_LRMD_ALERT_ID "lrmd_alert_id" #define F_LRMD_ALERT_PATH "lrmd_alert_path" #define F_LRMD_ALERT "lrmd_alert" #define LRMD_OP_RSC_REG "lrmd_rsc_register" #define LRMD_OP_RSC_EXEC "lrmd_rsc_exec" #define LRMD_OP_RSC_CANCEL "lrmd_rsc_cancel" #define LRMD_OP_RSC_UNREG "lrmd_rsc_unregister" #define LRMD_OP_RSC_INFO "lrmd_rsc_info" #define LRMD_OP_RSC_METADATA "lrmd_rsc_metadata" #define LRMD_OP_POKE "lrmd_rsc_poke" #define LRMD_OP_NEW_CLIENT "lrmd_rsc_new_client" #define LRMD_OP_CHECK "lrmd_check" #define LRMD_OP_ALERT_EXEC "lrmd_alert_exec" #define LRMD_OP_GET_RECURRING "lrmd_get_recurring" #define LRMD_IPC_OP_NEW "new" #define LRMD_IPC_OP_DESTROY "destroy" #define LRMD_IPC_OP_EVENT "event" #define LRMD_IPC_OP_REQUEST "request" #define LRMD_IPC_OP_RESPONSE "response" #define LRMD_IPC_OP_SHUTDOWN_REQ "shutdown_req" #define LRMD_IPC_OP_SHUTDOWN_ACK "shutdown_ack" #define LRMD_IPC_OP_SHUTDOWN_NACK "shutdown_nack" #define F_LRMD_IPC_OP "lrmd_ipc_op" #define F_LRMD_IPC_IPC_SERVER "lrmd_ipc_server" #define F_LRMD_IPC_SESSION "lrmd_ipc_session" #define F_LRMD_IPC_CLIENT "lrmd_ipc_client" #define F_LRMD_IPC_USER "lrmd_ipc_user" #define F_LRMD_IPC_MSG "lrmd_ipc_msg" #define F_LRMD_IPC_MSG_ID "lrmd_ipc_msg_id" #define F_LRMD_IPC_MSG_FLAGS "lrmd_ipc_msg_flags" #define T_LRMD "lrmd" #define T_LRMD_REPLY "lrmd_reply" #define T_LRMD_NOTIFY "lrmd_notify" #define T_LRMD_IPC_PROXY "lrmd_ipc_proxy" #define T_LRMD_RSC_OP "lrmd_rsc_op" /* *INDENT-ON* */ /*! * \brief Create a new connection to the local executor */ lrmd_t *lrmd_api_new(void); /*! * \brief Create a new TLS connection to a remote executor * * \param[in] nodename Name of remote node identified with this connection * \param[in] server Hostname to connect to * \param[in] port Port number to connect to (or 0 to use default) * * \return Newly created executor connection object * \note If only one of \p nodename and \p server is non-NULL, it will be used * for both purposes. If both are NULL, a local IPC connection will be * created instead. */ lrmd_t *lrmd_remote_api_new(const char *nodename, const char *server, int port); /*! * \brief Use after lrmd_poll returns 1 to read and dispatch a message * * \param[in,out] lrmd Executor connection object * * \return TRUE if connection is still up, FALSE if disconnected */ bool lrmd_dispatch(lrmd_t *lrmd); /*! * \brief Check whether a message is available on an executor connection * * \param[in,out] lrmd Executor connection object to check * \param[in] timeout Currently ignored * * \retval 1 Message is ready * \retval 0 Timeout occurred * \retval negative errno Error occurred * * \note This is intended for callers that do not use a main loop. */ int lrmd_poll(lrmd_t *lrmd, int timeout); /*! * \brief Destroy executor connection object * * \param[in,out] lrmd Executor connection object to destroy */ void lrmd_api_delete(lrmd_t *lrmd); lrmd_key_value_t *lrmd_key_value_add(lrmd_key_value_t * kvp, const char *key, const char *value); enum lrmd_call_options { lrmd_opt_none = 0, //! Notify only the client that made the request (rather than all clients) lrmd_opt_notify_orig_only = (1 << 1), /*! * Drop recurring operations initiated by a client when the client * disconnects. This option is only valid when registering a resource. When * used with a connection to a remote executor, recurring operations will be * dropped once all remote connections disconnect. * * @COMPAT This is broken, because these values should be unique bits, and * this value overlaps lrmd_opt_notify_orig_only (0x02). The impact is low * since this value is used only with registration requests and the other * one is used only with execution requests. Regardless, when we can break * API compatibility, this should be changed to (1 << 0) or (1 << 3). */ lrmd_opt_drop_recurring = 0x00000003, //! Send notifications for recurring operations only when the result changes lrmd_opt_notify_changes_only = (1 << 2), }; -enum lrmd_callback_event { - lrmd_event_register, - lrmd_event_unregister, - lrmd_event_exec_complete, - lrmd_event_disconnect, - lrmd_event_connect, - lrmd_event_poke, - lrmd_event_new_client, -}; - -typedef struct lrmd_event_data_s { - /*! Type of event, register, unregister, call_completed... */ - enum lrmd_callback_event type; - - /*! The resource this event occurred on. */ - const char *rsc_id; - /*! The action performed, start, stop, monitor... */ - const char *op_type; - /*! The user data passed by caller of exec() API function */ - const char *user_data; - - /*! The client api call id associated with this event */ - int call_id; - /*! The operation's timeout period in ms. */ - int timeout; - /*! The operation's recurring interval in ms. */ - guint interval_ms; - /*! The operation's start delay value in ms. */ - int start_delay; - /*! This operation that just completed is on a deleted rsc. */ - int rsc_deleted; - - /*! The executed ra return code mapped to OCF */ - enum ocf_exitcode rc; - /*! The executor status returned for exec_complete events */ - int op_status; - /*! stdout from resource agent operation */ - const char *output; - /*! Timestamp of when op ran */ - unsigned int t_run; - /*! Timestamp of last rc change */ - unsigned int t_rcchange; - /*! Time in length op took to execute */ - unsigned int exec_time; - /*! Time in length spent in queue */ - unsigned int queue_time; - - /*! int connection result. Used for connection and poke events */ - int connection_rc; - - /* This is a GHashTable containing the - * parameters given to the operation */ - void *params; - - /*! client node name associated with this connection - * (used to match actions to the proper client when there are multiple) - */ - const char *remote_nodename; - - /*! exit failure reason string from resource agent operation */ - const char *exit_reason; -} lrmd_event_data_t; - -lrmd_event_data_t *lrmd_new_event(const char *rsc_id, const char *task, - guint interval_ms); -lrmd_event_data_t *lrmd_copy_event(lrmd_event_data_t * event); -void lrmd_free_event(lrmd_event_data_t * event); - typedef struct lrmd_rsc_info_s { char *id; char *type; char *standard; char *provider; } lrmd_rsc_info_t; typedef struct lrmd_op_info_s { char *rsc_id; char *action; char *interval_ms_s; char *timeout_ms_s; } lrmd_op_info_t; lrmd_rsc_info_t *lrmd_new_rsc_info(const char *rsc_id, const char *standard, const char *provider, const char *type); lrmd_rsc_info_t *lrmd_copy_rsc_info(lrmd_rsc_info_t * rsc_info); void lrmd_free_rsc_info(lrmd_rsc_info_t * rsc_info); void lrmd_free_op_info(lrmd_op_info_t *op_info); typedef void (*lrmd_event_callback) (lrmd_event_data_t * event); typedef struct lrmd_list_s { const char *val; struct lrmd_list_s *next; } lrmd_list_t; void lrmd_list_freeall(lrmd_list_t * head); void lrmd_key_value_freeall(lrmd_key_value_t * head); typedef struct lrmd_api_operations_s { /*! * \brief Connect to an executor * * \param[in,out] lrmd Executor connection object * \param[in] client_name Arbitrary identifier to pass to server * \param[out] fd If not NULL, where to store file descriptor * for connection's socket * * \return Legacy Pacemaker return code */ int (*connect) (lrmd_t *lrmd, const char *client_name, int *fd); /*! * \brief Initiate an executor connection without blocking * * \param[in,out] lrmd Executor connection object * \param[in] client_name Arbitrary identifier to pass to server * \param[in] timeout Error if not connected within this time * (milliseconds) * * \return Legacy Pacemaker return code (if pcmk_ok, the event callback will * be called later with the result) * \note This function requires a mainloop. */ int (*connect_async) (lrmd_t *lrmd, const char *client_name, int timeout /*ms */ ); /*! * \brief Check whether connection to executor daemon is (still) active * * \param[in,out] lrmd Executor connection object to check * * \return 1 if the executor connection is active, 0 otherwise */ int (*is_connected) (lrmd_t *lrmd); /*! * \brief Poke executor connection to verify it is still active * * \param[in,out] lrmd Executor connection object to check * * \return Legacy Pacemaker return code (if pcmk_ok, the event callback will * be called later with the result) * \note The response comes in the form of a poke event to the callback. * */ int (*poke_connection) (lrmd_t *lrmd); /*! * \brief Disconnect from the executor. * * \param[in,out] lrmd Executor connection object to disconnect * * \return Legacy Pacemaker return code */ int (*disconnect) (lrmd_t *lrmd); /*! * \brief Register a resource with the executor * * \param[in,out] lrmd Executor connection object * \param[in] rsc_id ID of resource to register * \param[in] standard Resource's resource agent standard * \param[in] provider Resource's resource agent provider (or NULL) * \param[in] agent Resource's resource agent name * \param[in] options Group of enum lrmd_call_options flags * * \note Synchronous, guaranteed to occur in daemon before function returns. * * \return Legacy Pacemaker return code */ int (*register_rsc) (lrmd_t *lrmd, const char *rsc_id, const char *standard, const char *provider, const char *agent, enum lrmd_call_options options); /*! * \brief Retrieve a resource's registration information * * \param[in,out] lrmd Executor connection object * \param[in] rsc_id ID of resource to check * \param[in] options Group of enum lrmd_call_options flags * * \return Resource information on success, otherwise NULL */ lrmd_rsc_info_t *(*get_rsc_info) (lrmd_t *lrmd, const char *rsc_id, enum lrmd_call_options options); /*! * \brief Retrieve recurring operations registered for a resource * * \param[in,out] lrmd Executor connection object * \param[in] rsc_id ID of resource to check * \param[in] timeout_ms Error if not completed within this time * \param[in] options Group of enum lrmd_call_options flags * \param[out] output Where to store list of lrmd_op_info_t * * \return Legacy Pacemaker return code */ int (*get_recurring_ops) (lrmd_t *lrmd, const char *rsc_id, int timeout_ms, enum lrmd_call_options options, GList **output); /*! * \brief Unregister a resource from the executor * * \param[in,out] lrmd Executor connection object * \param[in] rsc_id ID of resource to unregister * \param[in] options Group of enum lrmd_call_options flags * * \return Legacy Pacemaker return code (of particular interest, EINPROGRESS * means that operations are in progress for the resource, and the * unregistration will be done when they complete) * \note Pending and recurring operations will be cancelled. * \note Synchronous, guaranteed to occur in daemon before function returns. * */ int (*unregister_rsc) (lrmd_t *lrmd, const char *rsc_id, enum lrmd_call_options options); /*! * \brief Set a callback for executor events * * \param[in,out] lrmd Executor connection object * \param[in] callback Callback to set */ void (*set_callback) (lrmd_t *lrmd, lrmd_event_callback callback); /*! * \brief Request execution of a resource action * * \param[in,out] lrmd Executor connection object * \param[in] rsc_id ID of resource * \param[in] action Name of resource action to execute * \param[in] userdata Arbitrary string to pass to event callback * \param[in] interval_ms If 0, execute action once, otherwise * recurring at this interval (in milliseconds) * \param[in] timeout Error if not complete within this time (in * milliseconds) * \param[in] start_delay Wait this long before execution (in * milliseconds) * \param[in] options Group of enum lrmd_call_options flags * \param[in,out] params Parameters to pass to agent (will be freed) * * \return A call ID for the action on success (in which case the action is * queued in the executor, and the event callback will be called * later with the result), otherwise a negative legacy Pacemaker * return code * \note exec() and cancel() operations on an individual resource are * guaranteed to occur in the order the client API is called. However, * operations on different resources are not guaranteed to occur in * any specific order. */ int (*exec) (lrmd_t *lrmd, const char *rsc_id, const char *action, const char *userdata, guint interval_ms, int timeout, int start_delay, enum lrmd_call_options options, lrmd_key_value_t *params); /*! * \brief Cancel a recurring resource action * * \param[in,out] lrmd Executor connection object * \param[in] rsc_id ID of resource * \param[in] action Name of resource action to cancel * \param[in] interval_ms Action's interval (in milliseconds) * * \return Legacy Pacemaker return code (if pcmk_ok, cancellation is queued * on function return, and the event callback will be called later * with an exec_complete event with an lrmd_op_status signifying * that the operation is cancelled) * * \note exec() and cancel() operations on an individual resource are * guaranteed to occur in the order the client API is called. However, * operations on different resources are not guaranteed to occur in * any specific order. */ int (*cancel) (lrmd_t *lrmd, const char *rsc_id, const char *action, guint interval_ms); /*! * \brief Retrieve resource agent metadata synchronously * * \param[in] lrmd Executor connection (unused) * \param[in] standard Resource agent class * \param[in] provider Resource agent provider * \param[in] agent Resource agent type * \param[out] output Where to store metadata (must not be NULL) * \param[in] options Group of enum lrmd_call_options flags (unused) * * \return Legacy Pacemaker return code * * \note Caller is responsible for freeing output. This call is always * synchronous (blocking), and always done directly by the library * (not via the executor connection). This means that it is based on * the local host environment, even if the executor connection is to a * remote node, so this may fail if the agent is not installed * locally. This also means that, if an external agent must be * executed, it will be executed by the caller's user, not the * executor's. */ int (*get_metadata) (lrmd_t *lrmd, const char *standard, const char *provider, const char *agent, char **output, enum lrmd_call_options options); /*! * \brief Retrieve a list of installed resource agents * * \param[in] lrmd Executor connection (unused) * \param[out] agents Where to store agent list (must not be NULL) * \param[in] standard Resource agent standard to list * \param[in] provider Resource agent provider to list (or NULL) * * \return Number of items in list on success, negative legacy Pacemaker * return code otherwise * * \note if standard is not provided, all known agents will be returned * \note list must be freed using lrmd_list_freeall() */ int (*list_agents) (lrmd_t *lrmd, lrmd_list_t **agents, const char *standard, const char *provider); /*! * \brief Retrieve a list of resource agent providers * * \param[in] lrmd Executor connection (unused) * \param[in] agent If not NULL, list providers for this agent only * \param[out] providers Where to store provider list * * \return Number of items in list on success, negative legacy Pacemaker * return code otherwise * \note The caller is responsible for freeing *providers with * lrmd_list_freeall(). */ int (*list_ocf_providers) (lrmd_t *lrmd, const char *agent, lrmd_list_t **providers); /*! * \brief Retrieve a list of supported standards * * \param[in] lrmd Executor connection (unused) * \param[out] standards Where to store standards list * * \return Number of items in list on success, negative legacy Pacemaker * return code otherwise * \note The caller is responsible for freeing *standards with * lrmd_list_freeall(). */ int (*list_standards) (lrmd_t *lrmd, lrmd_list_t **standards); /*! * \brief Execute an alert agent * * \param[in,out] lrmd Executor connection * \param[in] alert_id Name of alert to execute * \param[in] alert_path Full path to alert executable * \param[in] timeout Error if not complete within this many * milliseconds * \param[in,out] params Parameters to pass to agent (will be freed) * * \return Legacy Pacemaker return code (if pcmk_ok, the alert is queued in * the executor, and the event callback will be called later with * the result) * * \note Operations on individual alerts (by ID) are guaranteed to occur in * the order the client API is called. Operations on different alerts * are not guaranteed to occur in any specific order. */ int (*exec_alert) (lrmd_t *lrmd, const char *alert_id, const char *alert_path, int timeout, lrmd_key_value_t *params); /*! * \brief Retrieve resource agent metadata synchronously with parameters * * \param[in] lrmd Executor connection (unused) * \param[in] standard Resource agent class * \param[in] provider Resource agent provider * \param[in] agent Resource agent type * \param[out] output Where to store metadata (must not be NULL) * \param[in] options Group of enum lrmd_call_options flags (unused) * \param[in,out] params Parameters to pass to agent (will be freed) * * \return Legacy Pacemaker return code * * \note This is identical to the get_metadata() API call, except parameters * will be passed to the resource agent via environment variables. */ int (*get_metadata_params) (lrmd_t *lrmd, const char *standard, const char *provider, const char *agent, char **output, enum lrmd_call_options options, lrmd_key_value_t *params); } lrmd_api_operations_t; struct lrmd_s { lrmd_api_operations_t *cmds; void *lrmd_private; }; static inline const char * lrmd_event_type2str(enum lrmd_callback_event type) { switch (type) { case lrmd_event_register: return "register"; case lrmd_event_unregister: return "unregister"; case lrmd_event_exec_complete: return "exec_complete"; case lrmd_event_disconnect: return "disconnect"; case lrmd_event_connect: return "connect"; case lrmd_event_poke: return "poke"; case lrmd_event_new_client: return "new_client"; } return "unknown"; } #ifdef __cplusplus } #endif #endif diff --git a/include/crm/lrmd_events.h b/include/crm/lrmd_events.h new file mode 100644 index 0000000000..3a1c500cde --- /dev/null +++ b/include/crm/lrmd_events.h @@ -0,0 +1,108 @@ +/* + * Copyright 2012-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__CRM_LRMD_EVENTS__H +# define PCMK__CRM_LRMD_EVENTS__H + +#include // guint +#include // enum ocf_exitcode + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Resource agent executor events + * \ingroup lrmd + */ + +enum lrmd_callback_event { + lrmd_event_register, + lrmd_event_unregister, + lrmd_event_exec_complete, + lrmd_event_disconnect, + lrmd_event_connect, + lrmd_event_poke, + lrmd_event_new_client, +}; + +typedef struct lrmd_event_data_s { + /*! Type of event, register, unregister, call_completed... */ + enum lrmd_callback_event type; + + /*! The resource this event occurred on. */ + const char *rsc_id; + /*! The action performed, start, stop, monitor... */ + const char *op_type; + /*! The user data passed by caller of exec() API function */ + const char *user_data; + + /*! The client api call id associated with this event */ + int call_id; + + /*! The operation's timeout period in ms. */ + int timeout; + + /*! The operation's recurring interval in ms. */ + guint interval_ms; + + /*! The operation's start delay value in ms. */ + int start_delay; + + /*! This operation that just completed is on a deleted rsc. */ + int rsc_deleted; + + /*! The executed ra return code mapped to OCF */ + enum ocf_exitcode rc; + + /*! The executor status returned for exec_complete events */ + int op_status; + + /*! stdout from resource agent operation */ + const char *output; + + /*! Timestamp of when op ran */ + unsigned int t_run; + + /*! Timestamp of last rc change */ + unsigned int t_rcchange; + + /*! Time in length op took to execute */ + unsigned int exec_time; + + /*! Time in length spent in queue */ + unsigned int queue_time; + + /*! int connection result. Used for connection and poke events */ + int connection_rc; + + /* This is a GHashTable containing the + * parameters given to the operation */ + void *params; + + /*! client node name associated with this connection + * (used to match actions to the proper client when there are multiple) + */ + const char *remote_nodename; + + /*! exit failure reason string from resource agent operation */ + const char *exit_reason; +} lrmd_event_data_t; + +lrmd_event_data_t *lrmd_new_event(const char *rsc_id, const char *task, + guint interval_ms); +lrmd_event_data_t *lrmd_copy_event(lrmd_event_data_t *event); +void lrmd_free_event(lrmd_event_data_t *event); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_LRMD_EVENTS__H diff --git a/include/pcmki/pcmki_scheduler.h b/include/pcmki/pcmki_scheduler.h index d3b48f0785..0a2dc5174c 100644 --- a/include/pcmki/pcmki_scheduler.h +++ b/include/pcmki/pcmki_scheduler.h @@ -1,44 +1,44 @@ /* * Copyright 2014-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__PCMKI_PCMKI_SCHEDULER__H #define PCMK__PCMKI_PCMKI_SCHEDULER__H #include // GList #include // bool #include // xmlNode -#include // lrmd_event_data_t +#include // lrmd_event_data_t #include // pe_resource_t, pe_working_set_t typedef struct { const char *id; const char *node_attribute; pe_resource_t *dependent; // The resource being colocated pe_resource_t *primary; // The resource the dependent is colocated with int dependent_role; // Colocation applies only if dependent has this role int primary_role; // Colocation applies only if primary has this role int score; uint32_t flags; // Group of enum pcmk__coloc_flags } pcmk__colocation_t; void pcmk__unpack_constraints(pe_working_set_t *data_set); void pcmk__schedule_actions(xmlNode *cib, unsigned long long flags, pe_working_set_t *data_set); GList *pcmk__copy_node_list(const GList *list, bool reset); xmlNode *pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *event, const char *caller_version, int target_rc, const char *node, const char *origin); #endif diff --git a/include/pcmki/pcmki_transition.h b/include/pcmki/pcmki_transition.h index 85f2d61be9..93237ed2d5 100644 --- a/include/pcmki/pcmki_transition.h +++ b/include/pcmki/pcmki_transition.h @@ -1,177 +1,178 @@ /* * Copyright 2004-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__PCMKI_PCMKI_TRANSITION__H # define PCMK__PCMKI_PCMKI_TRANSITION__H # include # include # include # include +# include // lrmd_event_data_t #ifdef __cplusplus extern "C" { #endif enum pcmk__graph_action_type { pcmk__pseudo_graph_action, pcmk__rsc_graph_action, pcmk__cluster_graph_action, }; enum pcmk__synapse_flags { pcmk__synapse_ready = (1 << 0), pcmk__synapse_failed = (1 << 1), pcmk__synapse_executed = (1 << 2), pcmk__synapse_confirmed = (1 << 3), }; typedef struct { int id; int priority; uint32_t flags; // Group of pcmk__synapse_flags GList *actions; /* pcmk__graph_action_t* */ GList *inputs; /* pcmk__graph_action_t* */ } pcmk__graph_synapse_t; #define pcmk__set_synapse_flags(synapse, flags_to_set) do { \ (synapse)->flags = pcmk__set_flags_as(__func__, __LINE__, \ LOG_TRACE, \ "Synapse", "synapse", \ (synapse)->flags, (flags_to_set), #flags_to_set); \ } while (0) #define pcmk__clear_synapse_flags(synapse, flags_to_clear) do { \ (synapse)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ LOG_TRACE, \ "Synapse", "synapse", \ (synapse)->flags, (flags_to_clear), #flags_to_clear); \ } while (0) enum pcmk__graph_action_flags { pcmk__graph_action_sent_update = (1 << 0), /* sent to the CIB */ pcmk__graph_action_executed = (1 << 1), /* sent to the CRM */ pcmk__graph_action_confirmed = (1 << 2), pcmk__graph_action_failed = (1 << 3), pcmk__graph_action_can_fail = (1 << 4), //! \deprecated Will be removed in a future release }; typedef struct { int id; int timeout; int timer; guint interval_ms; GHashTable *params; enum pcmk__graph_action_type type; pcmk__graph_synapse_t *synapse; uint32_t flags; // Group of pcmk__graph_action_flags xmlNode *xml; } pcmk__graph_action_t; #define pcmk__set_graph_action_flags(action, flags_to_set) do { \ (action)->flags = pcmk__set_flags_as(__func__, __LINE__, \ LOG_TRACE, \ "Action", "action", \ (action)->flags, (flags_to_set), #flags_to_set); \ } while (0) #define pcmk__clear_graph_action_flags(action, flags_to_clear) do { \ (action)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ LOG_TRACE, \ "Action", "action", \ (action)->flags, (flags_to_clear), #flags_to_clear); \ } while (0) // What to do after finished processing a transition graph enum pcmk__graph_next { // Order matters: lowest priority to highest pcmk__graph_done, // Transition complete, nothing further needed pcmk__graph_wait, // Transition interrupted, wait for further changes pcmk__graph_restart, // Transition interrupted, start a new one pcmk__graph_shutdown, // Transition interrupted, local shutdown needed }; typedef struct { int id; char *source; int abort_priority; bool complete; const char *abort_reason; enum pcmk__graph_next completion_action; int num_actions; int num_synapses; int batch_limit; guint network_delay; guint stonith_timeout; int fired; int pending; int skipped; int completed; int incomplete; GList *synapses; /* pcmk__graph_synapse_t* */ int migration_limit; //! Failcount after one failed stop action char *failed_stop_offset; //! Failcount after one failed start action char *failed_start_offset; //! Time (from epoch) by which the controller should re-run the scheduler time_t recheck_by; } pcmk__graph_t; typedef struct { int (*pseudo) (pcmk__graph_t *graph, pcmk__graph_action_t *action); int (*rsc) (pcmk__graph_t *graph, pcmk__graph_action_t *action); int (*cluster) (pcmk__graph_t *graph, pcmk__graph_action_t *action); int (*fence) (pcmk__graph_t *graph, pcmk__graph_action_t *action); bool (*allowed) (pcmk__graph_t *graph, pcmk__graph_action_t *action); } pcmk__graph_functions_t; enum pcmk__graph_status { pcmk__graph_active, // Some actions have been performed pcmk__graph_pending, // No actions performed yet pcmk__graph_complete, pcmk__graph_terminated, }; void pcmk__set_graph_functions(pcmk__graph_functions_t *fns); pcmk__graph_t *pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference); enum pcmk__graph_status pcmk__execute_graph(pcmk__graph_t *graph); void pcmk__update_graph(pcmk__graph_t *graph, const pcmk__graph_action_t *action); void pcmk__free_graph(pcmk__graph_t *graph); const char *pcmk__graph_status2text(enum pcmk__graph_status state); void pcmk__log_graph(unsigned int log_level, pcmk__graph_t *graph); void pcmk__log_graph_action(int log_level, pcmk__graph_action_t *action); void pcmk__log_transition_summary(const char *filename); lrmd_event_data_t *pcmk__event_from_graph_action(const xmlNode *resource, const pcmk__graph_action_t *action, int status, int rc, const char *exit_reason); #ifdef __cplusplus } #endif #endif diff --git a/lib/pacemaker/libpacemaker_private.h b/lib/pacemaker/libpacemaker_private.h index db8ce54c39..48dead7df7 100644 --- a/lib/pacemaker/libpacemaker_private.h +++ b/lib/pacemaker/libpacemaker_private.h @@ -1,1138 +1,1139 @@ /* * Copyright 2021-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__LIBPACEMAKER_PRIVATE__H # define PCMK__LIBPACEMAKER_PRIVATE__H /* This header is for the sole use of libpacemaker, so that functions can be * declared with G_GNUC_INTERNAL for efficiency. */ +#include // lrmd_event_data_t #include // pe_action_t, pe_node_t, pe_working_set_t #include // pe__location_t // Colocation flags enum pcmk__coloc_flags { pcmk__coloc_none = 0U, // Primary is affected even if already active pcmk__coloc_influence = (1U << 0), // Colocation was explicitly configured in CIB pcmk__coloc_explicit = (1U << 1), }; // Flags to modify the behavior of add_colocated_node_scores() enum pcmk__coloc_select { // With no other flags, apply all "with this" colocations pcmk__coloc_select_default = 0, // Apply "this with" colocations instead of "with this" colocations pcmk__coloc_select_this_with = (1 << 0), // Apply only colocations with non-negative scores pcmk__coloc_select_nonnegative = (1 << 1), // Apply only colocations with at least one matching node pcmk__coloc_select_active = (1 << 2), }; // Flags the update_ordered_actions() method can return enum pcmk__updated { pcmk__updated_none = 0, // Nothing changed pcmk__updated_first = (1 << 0), // First action was updated pcmk__updated_then = (1 << 1), // Then action was updated }; #define pcmk__set_updated_flags(au_flags, action, flags_to_set) do { \ au_flags = pcmk__set_flags_as(__func__, __LINE__, \ LOG_TRACE, "Action update", \ (action)->uuid, au_flags, \ (flags_to_set), #flags_to_set); \ } while (0) #define pcmk__clear_updated_flags(au_flags, action, flags_to_clear) do { \ au_flags = pcmk__clear_flags_as(__func__, __LINE__, \ LOG_TRACE, "Action update", \ (action)->uuid, au_flags, \ (flags_to_clear), #flags_to_clear); \ } while (0) // Resource assignment methods struct resource_alloc_functions_s { /*! * \internal * \brief Assign a resource to a node * * \param[in,out] rsc Resource to assign to a node * \param[in] prefer Node to prefer, if all else is equal * \param[in] stop_if_fail If \c true and \p rsc can't be assigned to a * node, set next role to stopped and update * existing actions (if \p rsc is not a * primitive, this applies to its primitive * descendants instead) * * \return Node that \p rsc is assigned to, if assigned entirely to one node * * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() * can completely undo the assignment. A successful assignment can be * either undone or left alone as final. A failed assignment has the * same effect as calling pcmk__unassign_resource(); there are no side * effects on roles or actions. */ pe_node_t *(*assign)(pe_resource_t *rsc, const pe_node_t *prefer, bool stop_if_fail); /*! * \internal * \brief Create all actions needed for a given resource * * \param[in,out] rsc Resource to create actions for */ void (*create_actions)(pe_resource_t *rsc); /*! * \internal * \brief Schedule any probes needed for a resource on a node * * \param[in,out] rsc Resource to create probe for * \param[in,out] node Node to create probe on * * \return true if any probe was created, otherwise false */ bool (*create_probe)(pe_resource_t *rsc, pe_node_t *node); /*! * \internal * \brief Create implicit constraints needed for a resource * * \param[in,out] rsc Resource to create implicit constraints for */ void (*internal_constraints)(pe_resource_t *rsc); /*! * \internal * \brief Apply a colocation's score to node scores or resource priority * * Given a colocation constraint, apply its score to the dependent's * allowed node scores (if we are still placing resources) or priority (if * we are choosing promotable clone instance roles). * * \param[in,out] dependent Dependent resource in colocation * \param[in] primary Primary resource in colocation * \param[in] colocation Colocation constraint to apply * \param[in] for_dependent true if called on behalf of dependent */ void (*apply_coloc_score)(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent); /*! * \internal * \brief Create list of all resources in colocations with a given resource * * Given a resource, create a list of all resources involved in mandatory * colocations with it, whether directly or via chained colocations. * * \param[in] rsc Resource to add to colocated list * \param[in] orig_rsc Resource originally requested * \param[in,out] colocated_rscs Existing list * * \return List of given resource and all resources involved in colocations * * \note This function is recursive; top-level callers should pass NULL as * \p colocated_rscs and \p orig_rsc, and the desired resource as * \p rsc. The recursive calls will use other values. */ GList *(*colocated_resources)(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *colocated_rscs); /*! * \internal * \brief Add colocations affecting a resource as primary to a list * * Given a resource being assigned (\p orig_rsc) and a resource somewhere in * its chain of ancestors (\p rsc, which may be \p orig_rsc), get * colocations that affect the ancestor as primary and should affect the * resource, and add them to a given list. * * \param[in] rsc Resource whose colocations should be added * \param[in] orig_rsc Affected resource (\p rsc or a descendant) * \param[in,out] list List of colocations to add to * * \note All arguments should be non-NULL. * \note The pcmk__with_this_colocations() wrapper should usually be used * instead of using this method directly. */ void (*with_this_colocations)(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); /*! * \internal * \brief Add colocations affecting a resource as dependent to a list * * Given a resource being assigned (\p orig_rsc) and a resource somewhere in * its chain of ancestors (\p rsc, which may be \p orig_rsc), get * colocations that affect the ancestor as dependent and should affect the * resource, and add them to a given list. * * * \param[in] rsc Resource whose colocations should be added * \param[in] orig_rsc Affected resource (\p rsc or a descendant) * \param[in,out] list List of colocations to add to * * \note All arguments should be non-NULL. * \note The pcmk__this_with_colocations() wrapper should usually be used * instead of using this method directly. */ void (*this_with_colocations)(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); /*! * \internal * \brief Update nodes with scores of colocated resources' nodes * * Given a table of nodes and a resource, update the nodes' scores with the * scores of the best nodes matching the attribute used for each of the * resource's relevant colocations. * * \param[in,out] source_rsc Resource whose node scores to add * \param[in] target_rsc Resource on whose behalf to update \p *nodes * \param[in] log_id Resource ID for logs (if \c NULL, use * \p source_rsc ID) * \param[in,out] nodes Nodes to update (set initial contents to * \c NULL to copy allowed nodes from * \p source_rsc) * \param[in] colocation Original colocation constraint (used to get * configured primary resource's stickiness, and * to get colocation node attribute; if \c NULL, * source_rsc's own matching node scores * will not be added, and \p *nodes must be * \c NULL as well) * \param[in] factor Incorporate scores multiplied by this factor * \param[in] flags Bitmask of enum pcmk__coloc_select values * * \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation, * and the \c pcmk__coloc_select_this_with flag are used together (and * only by \c cmp_resources()). * \note The caller remains responsible for freeing \p *nodes. */ void (*add_colocated_node_scores)(pe_resource_t *source_rsc, const pe_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags); /*! * \internal * \brief Apply a location constraint to a resource's allowed node scores * * \param[in,out] rsc Resource to apply constraint to * \param[in,out] location Location constraint to apply */ void (*apply_location)(pe_resource_t *rsc, pe__location_t *location); /*! * \internal * \brief Return action flags for a given resource action * * \param[in,out] action Action to get flags for * \param[in] node If not NULL, limit effects to this node * * \return Flags appropriate to \p action on \p node * \note For primitives, this will be the same as action->flags regardless * of node. For collective resources, the flags can differ due to * multiple instances possibly being involved. */ uint32_t (*action_flags)(pe_action_t *action, const pe_node_t *node); /*! * \internal * \brief Update two actions according to an ordering between them * * Given information about an ordering of two actions, update the actions' * flags (and runnable_before members if appropriate) as appropriate for the * ordering. Effects may cascade to other orderings involving the actions as * well. * * \param[in,out] first 'First' action in an ordering * \param[in,out] then 'Then' action in an ordering * \param[in] node If not NULL, limit scope of ordering to this * node (only used when interleaving instances) * \param[in] flags Action flags for \p first for ordering purposes * \param[in] filter Action flags to limit scope of certain updates * (may include pe_action_optional to affect only * mandatory actions, and pe_action_runnable to * affect only runnable actions) * \param[in] type Group of enum pe_ordering flags to apply * \param[in,out] data_set Cluster working set * * \return Group of enum pcmk__updated flags indicating what was updated */ uint32_t (*update_ordered_actions)(pe_action_t *first, pe_action_t *then, const pe_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pe_working_set_t *data_set); /*! * \internal * \brief Output a summary of scheduled actions for a resource * * \param[in,out] rsc Resource to output actions for */ void (*output_actions)(pe_resource_t *rsc); /*! * \internal * \brief Add a resource's actions to the transition graph * * \param[in,out] rsc Resource whose actions should be added */ void (*add_actions_to_graph)(pe_resource_t *rsc); /*! * \internal * \brief Add meta-attributes relevant to transition graph actions to XML * * If a given resource supports variant-specific meta-attributes that are * needed for transition graph actions, add them to a given XML element. * * \param[in] rsc Resource whose meta-attributes should be added * \param[in,out] xml Transition graph action attributes XML to add to */ void (*add_graph_meta)(const pe_resource_t *rsc, xmlNode *xml); /*! * \internal * \brief Add a resource's utilization to a table of utilization values * * This function is used when summing the utilization of a resource and all * resources colocated with it, to determine whether a node has sufficient * capacity. Given a resource and a table of utilization values, it will add * the resource's utilization to the existing values, if the resource has * not yet been assigned to a node. * * \param[in] rsc Resource with utilization to add * \param[in] orig_rsc Resource being assigned (for logging only) * \param[in] all_rscs List of all resources that will be summed * \param[in,out] utilization Table of utilization values to add to */ void (*add_utilization)(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); /*! * \internal * \brief Apply a shutdown lock for a resource, if appropriate * * \param[in,out] rsc Resource to check for shutdown lock */ void (*shutdown_lock)(pe_resource_t *rsc); }; // Actions (pcmk_sched_actions.c) G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pe_action_t *action, pe_working_set_t *data_set); G_GNUC_INTERNAL uint32_t pcmk__update_ordered_actions(pe_action_t *first, pe_action_t *then, const pe_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pe_working_set_t *data_set); G_GNUC_INTERNAL void pcmk__log_action(const char *pre_text, const pe_action_t *action, bool details); G_GNUC_INTERNAL pe_action_t *pcmk__new_cancel_action(pe_resource_t *rsc, const char *name, guint interval_ms, const pe_node_t *node); G_GNUC_INTERNAL pe_action_t *pcmk__new_shutdown_action(pe_node_t *node); G_GNUC_INTERNAL bool pcmk__action_locks_rsc_to_node(const pe_action_t *action); G_GNUC_INTERNAL void pcmk__deduplicate_action_inputs(pe_action_t *action); G_GNUC_INTERNAL void pcmk__output_actions(pe_working_set_t *data_set); G_GNUC_INTERNAL bool pcmk__check_action_config(pe_resource_t *rsc, pe_node_t *node, const xmlNode *xml_op); G_GNUC_INTERNAL void pcmk__handle_rsc_config_changes(pe_working_set_t *data_set); // Recurring actions (pcmk_sched_recurring.c) G_GNUC_INTERNAL void pcmk__create_recurring_actions(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__schedule_cancel(pe_resource_t *rsc, const char *call_id, const char *task, guint interval_ms, const pe_node_t *node, const char *reason); G_GNUC_INTERNAL void pcmk__reschedule_recurring(pe_resource_t *rsc, const char *task, guint interval_ms, pe_node_t *node); G_GNUC_INTERNAL bool pcmk__action_is_recurring(const pe_action_t *action); // Producing transition graphs (pcmk_graph_producer.c) G_GNUC_INTERNAL bool pcmk__graph_has_loop(const pe_action_t *init_action, const pe_action_t *action, pe_action_wrapper_t *input); G_GNUC_INTERNAL void pcmk__add_rsc_actions_to_graph(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__create_graph(pe_working_set_t *data_set); // Fencing (pcmk_sched_fencing.c) G_GNUC_INTERNAL void pcmk__order_vs_fence(pe_action_t *stonith_op, pe_working_set_t *data_set); G_GNUC_INTERNAL void pcmk__order_vs_unfence(const pe_resource_t *rsc, pe_node_t *node, pe_action_t *action, enum pe_ordering order); G_GNUC_INTERNAL void pcmk__fence_guest(pe_node_t *node); G_GNUC_INTERNAL bool pcmk__node_unfenced(const pe_node_t *node); G_GNUC_INTERNAL void pcmk__order_restart_vs_unfence(gpointer data, gpointer user_data); // Injected scheduler inputs (pcmk_sched_injections.c) void pcmk__inject_scheduler_input(pe_working_set_t *data_set, cib_t *cib, const pcmk_injections_t *injections); // Constraints of any type (pcmk_sched_constraints.c) G_GNUC_INTERNAL pe_resource_t *pcmk__find_constraint_resource(GList *rsc_list, const char *id); G_GNUC_INTERNAL xmlNode *pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pe_working_set_t *data_set); G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(const pe_working_set_t *data_set, const char *id, pe_resource_t **rsc, pe_tag_t **tag); G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pe_working_set_t *data_set); G_GNUC_INTERNAL void pcmk__create_internal_constraints(pe_working_set_t *data_set); // Location constraints G_GNUC_INTERNAL void pcmk__unpack_location(xmlNode *xml_obj, pe_working_set_t *data_set); G_GNUC_INTERNAL pe__location_t *pcmk__new_location(const char *id, pe_resource_t *rsc, int node_score, const char *discover_mode, pe_node_t *foo_node); G_GNUC_INTERNAL void pcmk__apply_locations(pe_working_set_t *data_set); G_GNUC_INTERNAL void pcmk__apply_location(pe_resource_t *rsc, pe__location_t *constraint); // Colocation constraints (pcmk_sched_colocation.c) enum pcmk__coloc_affects { pcmk__coloc_affects_nothing = 0, pcmk__coloc_affects_location, pcmk__coloc_affects_role, }; /*! * \internal * \brief Get the value of a colocation's node attribute * * When looking up a colocation node attribute on a bundle node for a bundle * primitive, we should always look on the bundle node's assigned host, * regardless of the value of XML_RSC_ATTR_TARGET. At most one resource (the * bundle primitive, if any) can run on a bundle node, so any colocation must * necessarily be evaluated with respect to the bundle node (the container). * * \param[in] node Node on which to look up the attribute * \param[in] attr Name of attribute to look up * \param[in] rsc Resource on whose behalf to look up the attribute * * \return Value of \p attr on \p node or on the host of \p node, as appropriate */ static inline const char * pcmk__colocation_node_attr(const pe_node_t *node, const char *attr, const pe_resource_t *rsc) { const pe_resource_t *top = pe__const_top_resource(rsc, false); const bool force_host = pe__is_bundle_node(node) && pe_rsc_is_bundled(rsc) && (top == pe__bundled_resource(rsc)); return pe__node_attribute_calculated(node, attr, rsc, pe__rsc_node_assigned, force_host); } G_GNUC_INTERNAL enum pcmk__coloc_affects pcmk__colocation_affects(const pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation, bool preview); G_GNUC_INTERNAL void pcmk__apply_coloc_to_scores(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation); G_GNUC_INTERNAL void pcmk__apply_coloc_to_priority(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation); G_GNUC_INTERNAL void pcmk__add_colocated_node_scores(pe_resource_t *source_rsc, const pe_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags); G_GNUC_INTERNAL void pcmk__add_dependent_scores(gpointer data, gpointer user_data); G_GNUC_INTERNAL void pcmk__colocation_intersect_nodes(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation, const GList *primary_nodes, bool merge_scores); G_GNUC_INTERNAL void pcmk__unpack_colocation(xmlNode *xml_obj, pe_working_set_t *data_set); G_GNUC_INTERNAL void pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation, const pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__add_this_with_list(GList **list, GList *addition, const pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation, const pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__add_with_this_list(GList **list, GList *addition, const pe_resource_t *rsc); G_GNUC_INTERNAL GList *pcmk__with_this_colocations(const pe_resource_t *rsc); G_GNUC_INTERNAL GList *pcmk__this_with_colocations(const pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__new_colocation(const char *id, const char *node_attr, int score, pe_resource_t *dependent, pe_resource_t *primary, const char *dependent_role, const char *primary_role, uint32_t flags); G_GNUC_INTERNAL void pcmk__block_colocation_dependents(pe_action_t *action); /*! * \internal * \brief Check whether colocation's dependent preferences should be considered * * \param[in] colocation Colocation constraint * \param[in] rsc Primary instance (normally this will be * colocation->primary, which NULL will be treated as, * but for clones or bundles with multiple instances * this can be a particular instance) * * \return true if colocation influence should be effective, otherwise false */ static inline bool pcmk__colocation_has_influence(const pcmk__colocation_t *colocation, const pe_resource_t *rsc) { if (rsc == NULL) { rsc = colocation->primary; } /* A bundle replica colocates its remote connection with its container, * using a finite score so that the container can run on Pacemaker Remote * nodes. * * Moving a connection is lightweight and does not interrupt the service, * while moving a container is heavyweight and does interrupt the service, * so don't move a clean, active container based solely on the preferences * of its connection. * * This also avoids problematic scenarios where two containers want to * perpetually swap places. */ if (pcmk_is_set(colocation->dependent->flags, pe_rsc_allow_remote_remotes) && !pcmk_is_set(rsc->flags, pe_rsc_failed) && pcmk__list_of_1(rsc->running_on)) { return false; } /* The dependent in a colocation influences the primary's location * if the influence option is true or the primary is not yet active. */ return pcmk_is_set(colocation->flags, pcmk__coloc_influence) || (rsc->running_on == NULL); } // Ordering constraints (pcmk_sched_ordering.c) G_GNUC_INTERNAL void pcmk__new_ordering(pe_resource_t *first_rsc, char *first_task, pe_action_t *first_action, pe_resource_t *then_rsc, char *then_task, pe_action_t *then_action, uint32_t flags, pe_working_set_t *sched); G_GNUC_INTERNAL void pcmk__unpack_ordering(xmlNode *xml_obj, pe_working_set_t *data_set); G_GNUC_INTERNAL void pcmk__disable_invalid_orderings(pe_working_set_t *data_set); G_GNUC_INTERNAL void pcmk__order_stops_before_shutdown(pe_node_t *node, pe_action_t *shutdown_op); G_GNUC_INTERNAL void pcmk__apply_orderings(pe_working_set_t *sched); G_GNUC_INTERNAL void pcmk__order_after_each(pe_action_t *after, GList *list); /*! * \internal * \brief Create a new ordering between two resource actions * * \param[in,out] first_rsc Resource for 'first' action * \param[in,out] first_task Action key for 'first' action * \param[in] then_rsc Resource for 'then' action * \param[in,out] then_task Action key for 'then' action * \param[in] flags Bitmask of enum pe_ordering flags */ #define pcmk__order_resource_actions(first_rsc, first_task, \ then_rsc, then_task, flags) \ pcmk__new_ordering((first_rsc), \ pcmk__op_key((first_rsc)->id, (first_task), 0), \ NULL, \ (then_rsc), \ pcmk__op_key((then_rsc)->id, (then_task), 0), \ NULL, (flags), (first_rsc)->cluster) #define pcmk__order_starts(rsc1, rsc2, flags) \ pcmk__order_resource_actions((rsc1), PCMK_ACTION_START, \ (rsc2), PCMK_ACTION_START, (flags)) #define pcmk__order_stops(rsc1, rsc2, flags) \ pcmk__order_resource_actions((rsc1), PCMK_ACTION_STOP, \ (rsc2), PCMK_ACTION_STOP, (flags)) // Ticket constraints (pcmk_sched_tickets.c) G_GNUC_INTERNAL void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set); // Promotable clone resources (pcmk_sched_promotable.c) G_GNUC_INTERNAL void pcmk__add_promotion_scores(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__require_promotion_tickets(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__set_instance_roles(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__create_promotable_actions(pe_resource_t *clone); G_GNUC_INTERNAL void pcmk__promotable_restart_ordering(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__order_promotable_instances(pe_resource_t *clone); G_GNUC_INTERNAL void pcmk__update_dependent_with_promotable(const pe_resource_t *primary, pe_resource_t *dependent, const pcmk__colocation_t *colocation); G_GNUC_INTERNAL void pcmk__update_promotable_dependent_priority(const pe_resource_t *primary, pe_resource_t *dependent, const pcmk__colocation_t *colocation); // Pacemaker Remote nodes (pcmk_sched_remote.c) G_GNUC_INTERNAL bool pcmk__is_failed_remote_node(const pe_node_t *node); G_GNUC_INTERNAL void pcmk__order_remote_connection_actions(pe_working_set_t *data_set); G_GNUC_INTERNAL bool pcmk__rsc_corresponds_to_guest(const pe_resource_t *rsc, const pe_node_t *node); G_GNUC_INTERNAL pe_node_t *pcmk__connection_host_for_action(const pe_action_t *action); G_GNUC_INTERNAL void pcmk__substitute_remote_addr(pe_resource_t *rsc, GHashTable *params); G_GNUC_INTERNAL void pcmk__add_bundle_meta_to_xml(xmlNode *args_xml, const pe_action_t *action); // Primitives (pcmk_sched_primitive.c) G_GNUC_INTERNAL pe_node_t *pcmk__primitive_assign(pe_resource_t *rsc, const pe_node_t *prefer, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__primitive_create_actions(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__primitive_internal_constraints(pe_resource_t *rsc); G_GNUC_INTERNAL uint32_t pcmk__primitive_action_flags(pe_action_t *action, const pe_node_t *node); G_GNUC_INTERNAL void pcmk__primitive_apply_coloc_score(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent); G_GNUC_INTERNAL void pcmk__with_primitive_colocations(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__primitive_with_colocations(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__schedule_cleanup(pe_resource_t *rsc, const pe_node_t *node, bool optional); G_GNUC_INTERNAL void pcmk__primitive_add_graph_meta(const pe_resource_t *rsc, xmlNode *xml); G_GNUC_INTERNAL void pcmk__primitive_add_utilization(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); G_GNUC_INTERNAL void pcmk__primitive_shutdown_lock(pe_resource_t *rsc); // Groups (pcmk_sched_group.c) G_GNUC_INTERNAL pe_node_t *pcmk__group_assign(pe_resource_t *rsc, const pe_node_t *prefer, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__group_create_actions(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__group_internal_constraints(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__group_apply_coloc_score(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent); G_GNUC_INTERNAL void pcmk__with_group_colocations(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__group_with_colocations(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__group_add_colocated_node_scores(pe_resource_t *source_rsc, const pe_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags); G_GNUC_INTERNAL void pcmk__group_apply_location(pe_resource_t *rsc, pe__location_t *location); G_GNUC_INTERNAL uint32_t pcmk__group_action_flags(pe_action_t *action, const pe_node_t *node); G_GNUC_INTERNAL uint32_t pcmk__group_update_ordered_actions(pe_action_t *first, pe_action_t *then, const pe_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pe_working_set_t *data_set); G_GNUC_INTERNAL GList *pcmk__group_colocated_resources(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *colocated_rscs); G_GNUC_INTERNAL void pcmk__group_add_utilization(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); G_GNUC_INTERNAL void pcmk__group_shutdown_lock(pe_resource_t *rsc); // Clones (pcmk_sched_clone.c) G_GNUC_INTERNAL pe_node_t *pcmk__clone_assign(pe_resource_t *rsc, const pe_node_t *prefer, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__clone_create_actions(pe_resource_t *rsc); G_GNUC_INTERNAL bool pcmk__clone_create_probe(pe_resource_t *rsc, pe_node_t *node); G_GNUC_INTERNAL void pcmk__clone_internal_constraints(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__clone_apply_coloc_score(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent); G_GNUC_INTERNAL void pcmk__with_clone_colocations(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__clone_with_colocations(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__clone_apply_location(pe_resource_t *rsc, pe__location_t *constraint); G_GNUC_INTERNAL uint32_t pcmk__clone_action_flags(pe_action_t *action, const pe_node_t *node); G_GNUC_INTERNAL void pcmk__clone_add_actions_to_graph(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__clone_add_graph_meta(const pe_resource_t *rsc, xmlNode *xml); G_GNUC_INTERNAL void pcmk__clone_add_utilization(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); G_GNUC_INTERNAL void pcmk__clone_shutdown_lock(pe_resource_t *rsc); // Bundles (pcmk_sched_bundle.c) G_GNUC_INTERNAL pe_node_t *pcmk__bundle_assign(pe_resource_t *rsc, const pe_node_t *prefer, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__bundle_create_actions(pe_resource_t *rsc); G_GNUC_INTERNAL bool pcmk__bundle_create_probe(pe_resource_t *rsc, pe_node_t *node); G_GNUC_INTERNAL void pcmk__bundle_internal_constraints(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__bundle_apply_coloc_score(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent); G_GNUC_INTERNAL void pcmk__with_bundle_colocations(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__bundle_with_colocations(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList **list); G_GNUC_INTERNAL void pcmk__bundle_apply_location(pe_resource_t *rsc, pe__location_t *constraint); G_GNUC_INTERNAL uint32_t pcmk__bundle_action_flags(pe_action_t *action, const pe_node_t *node); G_GNUC_INTERNAL void pcmk__output_bundle_actions(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__bundle_add_actions_to_graph(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__bundle_add_utilization(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization); G_GNUC_INTERNAL void pcmk__bundle_shutdown_lock(pe_resource_t *rsc); // Clone instances or bundle replica containers (pcmk_sched_instances.c) G_GNUC_INTERNAL void pcmk__assign_instances(pe_resource_t *collective, GList *instances, int max_total, int max_per_node); G_GNUC_INTERNAL void pcmk__create_instance_actions(pe_resource_t *rsc, GList *instances); G_GNUC_INTERNAL bool pcmk__instance_matches(const pe_resource_t *instance, const pe_node_t *node, enum rsc_role_e role, bool current); G_GNUC_INTERNAL pe_resource_t *pcmk__find_compatible_instance(const pe_resource_t *match_rsc, const pe_resource_t *rsc, enum rsc_role_e role, bool current); G_GNUC_INTERNAL uint32_t pcmk__instance_update_ordered_actions(pe_action_t *first, pe_action_t *then, const pe_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pe_working_set_t *data_set); G_GNUC_INTERNAL uint32_t pcmk__collective_action_flags(pe_action_t *action, const GList *instances, const pe_node_t *node); // Injections (pcmk_injections.c) G_GNUC_INTERNAL xmlNode *pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid); G_GNUC_INTERNAL xmlNode *pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up); G_GNUC_INTERNAL xmlNode *pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node, const char *resource, const char *lrm_name, const char *rclass, const char *rtype, const char *rprovider); G_GNUC_INTERNAL void pcmk__inject_failcount(pcmk__output_t *out, xmlNode *cib_node, const char *resource, const char *task, guint interval_ms, int rc); G_GNUC_INTERNAL xmlNode *pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op, int target_rc); // Nodes (pcmk_sched_nodes.c) G_GNUC_INTERNAL bool pcmk__node_available(const pe_node_t *node, bool consider_score, bool consider_guest); G_GNUC_INTERNAL bool pcmk__any_node_available(GHashTable *nodes); G_GNUC_INTERNAL GHashTable *pcmk__copy_node_table(GHashTable *nodes); G_GNUC_INTERNAL void pcmk__copy_node_tables(const pe_resource_t *rsc, GHashTable **copy); G_GNUC_INTERNAL void pcmk__restore_node_tables(pe_resource_t *rsc, GHashTable *backup); G_GNUC_INTERNAL GList *pcmk__sort_nodes(GList *nodes, pe_node_t *active_node); G_GNUC_INTERNAL void pcmk__apply_node_health(pe_working_set_t *data_set); G_GNUC_INTERNAL pe_node_t *pcmk__top_allowed_node(const pe_resource_t *rsc, const pe_node_t *node); // Functions applying to more than one variant (pcmk_sched_resource.c) G_GNUC_INTERNAL void pcmk__set_assignment_methods(pe_working_set_t *data_set); G_GNUC_INTERNAL bool pcmk__rsc_agent_changed(pe_resource_t *rsc, pe_node_t *node, const xmlNode *rsc_entry, bool active_on_node); G_GNUC_INTERNAL GList *pcmk__rscs_matching_id(const char *id, const pe_working_set_t *data_set); G_GNUC_INTERNAL GList *pcmk__colocated_resources(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *colocated_rscs); G_GNUC_INTERNAL void pcmk__noop_add_graph_meta(const pe_resource_t *rsc, xmlNode *xml); G_GNUC_INTERNAL void pcmk__output_resource_actions(pe_resource_t *rsc); G_GNUC_INTERNAL bool pcmk__assign_resource(pe_resource_t *rsc, pe_node_t *node, bool force, bool stop_if_fail); G_GNUC_INTERNAL void pcmk__unassign_resource(pe_resource_t *rsc); G_GNUC_INTERNAL bool pcmk__threshold_reached(pe_resource_t *rsc, const pe_node_t *node, pe_resource_t **failed); G_GNUC_INTERNAL void pcmk__sort_resources(pe_working_set_t *data_set); G_GNUC_INTERNAL gint pcmk__cmp_instance(gconstpointer a, gconstpointer b); G_GNUC_INTERNAL gint pcmk__cmp_instance_number(gconstpointer a, gconstpointer b); // Functions related to probes (pcmk_sched_probes.c) G_GNUC_INTERNAL bool pcmk__probe_rsc_on_node(pe_resource_t *rsc, pe_node_t *node); G_GNUC_INTERNAL void pcmk__order_probes(pe_working_set_t *data_set); G_GNUC_INTERNAL bool pcmk__probe_resource_list(GList *rscs, pe_node_t *node); G_GNUC_INTERNAL void pcmk__schedule_probes(pe_working_set_t *data_set); // Functions related to live migration (pcmk_sched_migration.c) void pcmk__create_migration_actions(pe_resource_t *rsc, const pe_node_t *current); void pcmk__abort_dangling_migration(void *data, void *user_data); bool pcmk__rsc_can_migrate(const pe_resource_t *rsc, const pe_node_t *current); void pcmk__order_migration_equivalents(pe__ordering_t *order); // Functions related to node utilization (pcmk_sched_utilization.c) G_GNUC_INTERNAL int pcmk__compare_node_capacities(const pe_node_t *node1, const pe_node_t *node2); G_GNUC_INTERNAL void pcmk__consume_node_capacity(GHashTable *current_utilization, const pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__release_node_capacity(GHashTable *current_utilization, const pe_resource_t *rsc); G_GNUC_INTERNAL const pe_node_t *pcmk__ban_insufficient_capacity(pe_resource_t *rsc); G_GNUC_INTERNAL void pcmk__create_utilization_constraints(pe_resource_t *rsc, const GList *allowed_nodes); G_GNUC_INTERNAL void pcmk__show_node_capacities(const char *desc, pe_working_set_t *data_set); #endif // PCMK__LIBPACEMAKER_PRIVATE__H diff --git a/lib/pacemaker/pcmk_injections.c b/lib/pacemaker/pcmk_injections.c index ab35b6fe2b..599f87b143 100644 --- a/lib/pacemaker/pcmk_injections.c +++ b/lib/pacemaker/pcmk_injections.c @@ -1,788 +1,788 @@ /* * Copyright 2009-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include -#include // lrmd_event_data_t, lrmd_free_event() #include #include #include #include #include +#include // lrmd_event_data_t, etc. #include #include #include #include "libpacemaker_private.h" bool pcmk__simulate_node_config = false; #define XPATH_NODE_CONFIG "//" XML_CIB_TAG_NODE "[@" XML_ATTR_UNAME "='%s']" #define XPATH_NODE_STATE "//" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" #define XPATH_NODE_STATE_BY_ID "//" XML_CIB_TAG_STATE "[@" XML_ATTR_ID "='%s']" #define XPATH_RSC_HISTORY XPATH_NODE_STATE \ "//" XML_LRM_TAG_RESOURCE "[@" XML_ATTR_ID "='%s']" /*! * \internal * \brief Inject a fictitious transient node attribute into scheduler input * * \param[in,out] out Output object for displaying error messages * \param[in,out] cib_node node_state XML to inject attribute into * \param[in] name Transient node attribute name to inject * \param[in] value Transient node attribute value to inject */ static void inject_transient_attr(pcmk__output_t *out, xmlNode *cib_node, const char *name, const char *value) { xmlNode *attrs = NULL; xmlNode *instance_attrs = NULL; const char *node_uuid = ID(cib_node); out->message(out, "inject-attr", name, value, cib_node); attrs = first_named_child(cib_node, XML_TAG_TRANSIENT_NODEATTRS); if (attrs == NULL) { attrs = create_xml_node(cib_node, XML_TAG_TRANSIENT_NODEATTRS); crm_xml_add(attrs, XML_ATTR_ID, node_uuid); } instance_attrs = first_named_child(attrs, XML_TAG_ATTR_SETS); if (instance_attrs == NULL) { instance_attrs = create_xml_node(attrs, XML_TAG_ATTR_SETS); crm_xml_add(instance_attrs, XML_ATTR_ID, node_uuid); } crm_create_nvpair_xml(instance_attrs, NULL, name, value); } /*! * \internal * \brief Inject a fictitious fail count into a scheduler input * * \param[in,out] out Output object for displaying error messages * \param[in,out] cib_node Node state XML to inject into * \param[in] resource ID of resource for fail count to inject * \param[in] task Action name for fail count to inject * \param[in] interval_ms Action interval (in milliseconds) for fail count * \param[in] rc Action result for fail count to inject (if 0, or * 7 when interval_ms is 0, inject nothing) */ void pcmk__inject_failcount(pcmk__output_t *out, xmlNode *cib_node, const char *resource, const char *task, guint interval_ms, int rc) { if (rc == 0) { return; } else if ((rc == 7) && (interval_ms == 0)) { return; } else { char *name = NULL; char *now = pcmk__ttoa(time(NULL)); name = pcmk__failcount_name(resource, task, interval_ms); inject_transient_attr(out, cib_node, name, "value++"); free(name); name = pcmk__lastfailure_name(resource, task, interval_ms); inject_transient_attr(out, cib_node, name, now); free(name); free(now); } } /*! * \internal * \brief Create a CIB configuration entry for a fictitious node * * \param[in,out] cib_conn CIB object to use * \param[in] node Node name to use */ static void create_node_entry(cib_t *cib_conn, const char *node) { int rc = pcmk_ok; char *xpath = crm_strdup_printf(XPATH_NODE_CONFIG, node); rc = cib_conn->cmds->query(cib_conn, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); if (rc == -ENXIO) { // Only add if not already existing xmlNode *cib_object = create_xml_node(NULL, XML_CIB_TAG_NODE); crm_xml_add(cib_object, XML_ATTR_ID, node); // Use node name as ID crm_xml_add(cib_object, XML_ATTR_UNAME, node); cib_conn->cmds->create(cib_conn, XML_CIB_TAG_NODES, cib_object, cib_sync_call|cib_scope_local); /* Not bothering with subsequent query to see if it exists, we'll bomb out later in the call to query_node_uuid()... */ free_xml(cib_object); } free(xpath); } /*! * \internal * \brief Synthesize a fake executor event for an action * * \param[in] cib_resource XML for any existing resource action history * \param[in] task Name of action to synthesize * \param[in] interval_ms Interval of action to synthesize * \param[in] outcome Result of action to synthesize * * \return Newly allocated executor event * \note It is the caller's responsibility to free the result with * lrmd_free_event(). */ static lrmd_event_data_t * create_op(const xmlNode *cib_resource, const char *task, guint interval_ms, int outcome) { lrmd_event_data_t *op = NULL; xmlNode *xop = NULL; op = lrmd_new_event(ID(cib_resource), task, interval_ms); lrmd__set_result(op, outcome, PCMK_EXEC_DONE, "Simulated action result"); op->params = NULL; // Not needed for simulation purposes op->t_run = (unsigned int) time(NULL); op->t_rcchange = op->t_run; // Use a call ID higher than any existing history entries op->call_id = 0; for (xop = pcmk__xe_first_child(cib_resource); xop != NULL; xop = pcmk__xe_next(xop)) { int tmp = 0; crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); if (tmp > op->call_id) { op->call_id = tmp; } } op->call_id++; return op; } /*! * \internal * \brief Inject a fictitious resource history entry into a scheduler input * * \param[in,out] cib_resource Resource history XML to inject entry into * \param[in,out] op Action result to inject * \param[in] target_rc Expected result for action to inject * * \return XML of injected resource history entry */ xmlNode * pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op, int target_rc) { return pcmk__create_history_xml(cib_resource, op, CRM_FEATURE_SET, target_rc, NULL, crm_system_name); } /*! * \internal * \brief Inject a fictitious node into a scheduler input * * \param[in,out] cib_conn Scheduler input CIB to inject node into * \param[in] node Name of node to inject * \param[in] uuid UUID of node to inject * * \return XML of node_state entry for new node * \note If the global pcmk__simulate_node_config has been set to true, a * node entry in the configuration section will be added, as well as a * node state entry in the status section. */ xmlNode * pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid) { int rc = pcmk_ok; xmlNode *cib_object = NULL; char *xpath = crm_strdup_printf(XPATH_NODE_STATE, node); bool duplicate = false; char *found_uuid = NULL; if (pcmk__simulate_node_config) { create_node_entry(cib_conn, node); } rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); if ((cib_object != NULL) && (ID(cib_object) == NULL)) { crm_err("Detected multiple node_state entries for xpath=%s, bailing", xpath); duplicate = true; goto done; } if (rc == -ENXIO) { if (uuid == NULL) { query_node_uuid(cib_conn, node, &found_uuid, NULL); } else { found_uuid = strdup(uuid); } if (found_uuid) { char *xpath_by_uuid = crm_strdup_printf(XPATH_NODE_STATE_BY_ID, found_uuid); // It's possible that a node_state entry doesn't have an uname yet. rc = cib_conn->cmds->query(cib_conn, xpath_by_uuid, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); if ((cib_object != NULL) && (ID(cib_object) == NULL)) { crm_err("Can't inject node state for %s because multiple " "state entries found for ID %s", node, found_uuid); duplicate = true; free(xpath_by_uuid); goto done; } else if (cib_object != NULL) { crm_xml_add(cib_object, XML_ATTR_UNAME, node); rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_STATUS, cib_object, cib_sync_call|cib_scope_local); } free(xpath_by_uuid); } } if (rc == -ENXIO) { cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE); crm_xml_add(cib_object, XML_ATTR_ID, found_uuid); crm_xml_add(cib_object, XML_ATTR_UNAME, node); cib_conn->cmds->create(cib_conn, XML_CIB_TAG_STATUS, cib_object, cib_sync_call|cib_scope_local); free_xml(cib_object); rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); crm_trace("Injecting node state for %s (rc=%d)", node, rc); } done: free(found_uuid); free(xpath); if (duplicate) { crm_log_xml_warn(cib_object, "Duplicates"); crm_exit(CRM_EX_SOFTWARE); return NULL; // not reached, but makes static analysis happy } CRM_ASSERT(rc == pcmk_ok); return cib_object; } /*! * \internal * \brief Inject a fictitious node state change into a scheduler input * * \param[in,out] cib_conn Scheduler input CIB to inject into * \param[in] node Name of node to inject change for * \param[in] up If true, change state to online, otherwise offline * * \return XML of changed (or added) node state entry */ xmlNode * pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up) { xmlNode *cib_node = pcmk__inject_node(cib_conn, node, NULL); if (up) { pcmk__xe_set_props(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_YES, XML_NODE_IS_PEER, ONLINESTATUS, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_MEMBER, XML_NODE_EXPECTED, CRMD_JOINSTATE_MEMBER, NULL); } else { pcmk__xe_set_props(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_NO, XML_NODE_IS_PEER, OFFLINESTATUS, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_DOWN, XML_NODE_EXPECTED, CRMD_JOINSTATE_DOWN, NULL); } crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name); return cib_node; } /*! * \internal * \brief Check whether a node has history for a given resource * * \param[in,out] cib_node Node state XML to check * \param[in] resource Resource name to check for * * \return Resource's lrm_resource XML entry beneath \p cib_node if found, * otherwise NULL */ static xmlNode * find_resource_xml(xmlNode *cib_node, const char *resource) { const char *node = crm_element_value(cib_node, XML_ATTR_UNAME); char *xpath = crm_strdup_printf(XPATH_RSC_HISTORY, node, resource); xmlNode *match = get_xpath_object(xpath, cib_node, LOG_TRACE); free(xpath); return match; } /*! * \internal * \brief Inject a resource history element into a scheduler input * * \param[in,out] out Output object for displaying error messages * \param[in,out] cib_node Node state XML to inject resource history entry into * \param[in] resource ID (in configuration) of resource to inject * \param[in] lrm_name ID as used in history (could be clone instance) * \param[in] rclass Resource agent class of resource to inject * \param[in] rtype Resource agent type of resource to inject * \param[in] rprovider Resource agent provider of resource to inject * * \return XML of injected resource history element * \note If a history element already exists under either \p resource or * \p lrm_name, this will return it rather than injecting a new one. */ xmlNode * pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node, const char *resource, const char *lrm_name, const char *rclass, const char *rtype, const char *rprovider) { xmlNode *lrm = NULL; xmlNode *container = NULL; xmlNode *cib_resource = NULL; cib_resource = find_resource_xml(cib_node, resource); if (cib_resource != NULL) { /* If an existing LRM history entry uses the resource name, * continue using it, even if lrm_name is different. */ return cib_resource; } // Check for history entry under preferred name if (strcmp(resource, lrm_name) != 0) { cib_resource = find_resource_xml(cib_node, lrm_name); if (cib_resource != NULL) { return cib_resource; } } if ((rclass == NULL) || (rtype == NULL)) { // @TODO query configuration for class, provider, type out->err(out, "Resource %s not found in the status section of %s " "(supply class and type to continue)", resource, ID(cib_node)); return NULL; } else if (!pcmk__strcase_any_of(rclass, PCMK_RESOURCE_CLASS_OCF, PCMK_RESOURCE_CLASS_STONITH, PCMK_RESOURCE_CLASS_SERVICE, PCMK_RESOURCE_CLASS_UPSTART, PCMK_RESOURCE_CLASS_SYSTEMD, PCMK_RESOURCE_CLASS_LSB, NULL)) { out->err(out, "Invalid class for %s: %s", resource, rclass); return NULL; } else if (pcmk_is_set(pcmk_get_ra_caps(rclass), pcmk_ra_cap_provider) && (rprovider == NULL)) { // @TODO query configuration for provider out->err(out, "Please specify the provider for resource %s", resource); return NULL; } crm_info("Injecting new resource %s into node state '%s'", lrm_name, ID(cib_node)); lrm = first_named_child(cib_node, XML_CIB_TAG_LRM); if (lrm == NULL) { const char *node_uuid = ID(cib_node); lrm = create_xml_node(cib_node, XML_CIB_TAG_LRM); crm_xml_add(lrm, XML_ATTR_ID, node_uuid); } container = first_named_child(lrm, XML_LRM_TAG_RESOURCES); if (container == NULL) { container = create_xml_node(lrm, XML_LRM_TAG_RESOURCES); } cib_resource = create_xml_node(container, XML_LRM_TAG_RESOURCE); // If we're creating a new entry, use the preferred name crm_xml_add(cib_resource, XML_ATTR_ID, lrm_name); crm_xml_add(cib_resource, XML_AGENT_ATTR_CLASS, rclass); crm_xml_add(cib_resource, XML_AGENT_ATTR_PROVIDER, rprovider); crm_xml_add(cib_resource, XML_ATTR_TYPE, rtype); return cib_resource; } static int find_ticket_state(pcmk__output_t *out, cib_t *the_cib, const char *ticket_id, xmlNode **ticket_state_xml) { int rc = pcmk_ok; xmlNode *xml_search = NULL; GString *xpath = g_string_sized_new(256); CRM_ASSERT(ticket_state_xml != NULL); *ticket_state_xml = NULL; g_string_append(xpath, "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS "/" XML_CIB_TAG_TICKETS); if (ticket_id) { pcmk__g_strcat(xpath, "/" XML_CIB_TAG_TICKET_STATE "[@" XML_ATTR_ID "=\"", ticket_id, "\"]", NULL); } rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search, cib_sync_call|cib_scope_local|cib_xpath); g_string_free(xpath, TRUE); if (rc != pcmk_ok) { return rc; } crm_log_xml_debug(xml_search, "Match"); if ((xml_search->children != NULL) && (ticket_id != NULL)) { out->err(out, "Multiple ticket_states match ticket_id=%s", ticket_id); } *ticket_state_xml = xml_search; return rc; } /*! * \internal * \brief Inject a ticket attribute into ticket state * * \param[in,out] out Output object for displaying error messages * \param[in] ticket_id Ticket whose state should be changed * \param[in] attr_name Ticket attribute name to inject * \param[in] attr_value Boolean value of ticket attribute to inject * \param[in,out] cib CIB object to use * * \return Standard Pacemaker return code */ static int set_ticket_state_attr(pcmk__output_t *out, const char *ticket_id, const char *attr_name, bool attr_value, cib_t *cib) { int rc = pcmk_rc_ok; xmlNode *xml_top = NULL; xmlNode *ticket_state_xml = NULL; // Check for an existing ticket state entry rc = find_ticket_state(out, cib, ticket_id, &ticket_state_xml); rc = pcmk_legacy2rc(rc); if (rc == pcmk_rc_ok) { // Ticket state found, use it crm_debug("Injecting attribute into existing ticket state %s", ticket_id); xml_top = ticket_state_xml; } else if (rc == ENXIO) { // No ticket state, create it xmlNode *xml_obj = NULL; xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS); xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS); ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE); crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id); } else { // Error return rc; } // Add the attribute to the ticket state pcmk__xe_set_bool_attr(ticket_state_xml, attr_name, attr_value); crm_log_xml_debug(xml_top, "Update"); // Commit the change to the CIB rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_sync_call|cib_scope_local); rc = pcmk_legacy2rc(rc); free_xml(xml_top); return rc; } /*! * \internal * \brief Inject a fictitious action into the cluster * * \param[in,out] out Output object for displaying error messages * \param[in] spec Action specification to inject * \param[in,out] cib CIB object for scheduler input * \param[in] data_set Cluster working set */ static void inject_action(pcmk__output_t *out, const char *spec, cib_t *cib, const pe_working_set_t *data_set) { int rc; int outcome = PCMK_OCF_OK; guint interval_ms = 0; char *key = NULL; char *node = NULL; char *task = NULL; char *resource = NULL; const char *rtype = NULL; const char *rclass = NULL; const char *rprovider = NULL; xmlNode *cib_op = NULL; xmlNode *cib_node = NULL; xmlNode *cib_resource = NULL; const pe_resource_t *rsc = NULL; lrmd_event_data_t *op = NULL; out->message(out, "inject-spec", spec); key = calloc(1, strlen(spec) + 1); node = calloc(1, strlen(spec) + 1); rc = sscanf(spec, "%[^@]@%[^=]=%d", key, node, &outcome); if (rc != 3) { out->err(out, "Invalid operation spec: %s. Only found %d fields", spec, rc); goto done; } parse_op_key(key, &resource, &task, &interval_ms); rsc = pe_find_resource(data_set->resources, resource); if (rsc == NULL) { out->err(out, "Invalid resource name: %s", resource); goto done; } rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); rprovider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); cib_node = pcmk__inject_node(cib, node, NULL); CRM_ASSERT(cib_node != NULL); pcmk__inject_failcount(out, cib_node, resource, task, interval_ms, outcome); cib_resource = pcmk__inject_resource_history(out, cib_node, resource, resource, rclass, rtype, rprovider); CRM_ASSERT(cib_resource != NULL); op = create_op(cib_resource, task, interval_ms, outcome); CRM_ASSERT(op != NULL); cib_op = pcmk__inject_action_result(cib_resource, op, 0); CRM_ASSERT(cib_op != NULL); lrmd_free_event(op); rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); done: free(task); free(node); free(key); } /*! * \internal * \brief Inject fictitious scheduler inputs * * \param[in,out] data_set Cluster working set * \param[in,out] cib CIB object for scheduler input to modify * \param[in] injections Injections to apply */ void pcmk__inject_scheduler_input(pe_working_set_t *data_set, cib_t *cib, const pcmk_injections_t *injections) { int rc = pcmk_ok; const GList *iter = NULL; xmlNode *cib_node = NULL; pcmk__output_t *out = data_set->priv; out->message(out, "inject-modify-config", injections->quorum, injections->watchdog); if (injections->quorum != NULL) { xmlNode *top = create_xml_node(NULL, XML_TAG_CIB); /* crm_xml_add(top, XML_ATTR_DC_UUID, dc_uuid); */ crm_xml_add(top, XML_ATTR_HAVE_QUORUM, injections->quorum); rc = cib->cmds->modify(cib, NULL, top, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); } if (injections->watchdog != NULL) { rc = cib__update_node_attr(out, cib, cib_sync_call|cib_scope_local, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL, XML_ATTR_HAVE_WATCHDOG, injections->watchdog, NULL, NULL); CRM_ASSERT(rc == pcmk_rc_ok); } for (iter = injections->node_up; iter != NULL; iter = iter->next) { const char *node = (const char *) iter->data; out->message(out, "inject-modify-node", "Online", node); cib_node = pcmk__inject_node_state_change(cib, node, true); CRM_ASSERT(cib_node != NULL); rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); free_xml(cib_node); } for (iter = injections->node_down; iter != NULL; iter = iter->next) { const char *node = (const char *) iter->data; char *xpath = NULL; out->message(out, "inject-modify-node", "Offline", node); cib_node = pcmk__inject_node_state_change(cib, node, false); CRM_ASSERT(cib_node != NULL); rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); free_xml(cib_node); xpath = crm_strdup_printf("//node_state[@uname='%s']/%s", node, XML_CIB_TAG_LRM); cib->cmds->remove(cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); free(xpath); xpath = crm_strdup_printf("//node_state[@uname='%s']/%s", node, XML_TAG_TRANSIENT_NODEATTRS); cib->cmds->remove(cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); free(xpath); } for (iter = injections->node_fail; iter != NULL; iter = iter->next) { const char *node = (const char *) iter->data; out->message(out, "inject-modify-node", "Failing", node); cib_node = pcmk__inject_node_state_change(cib, node, true); crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_NO); CRM_ASSERT(cib_node != NULL); rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == pcmk_ok); free_xml(cib_node); } for (iter = injections->ticket_grant; iter != NULL; iter = iter->next) { const char *ticket_id = (const char *) iter->data; out->message(out, "inject-modify-ticket", "Granting", ticket_id); rc = set_ticket_state_attr(out, ticket_id, "granted", true, cib); CRM_ASSERT(rc == pcmk_rc_ok); } for (iter = injections->ticket_revoke; iter != NULL; iter = iter->next) { const char *ticket_id = (const char *) iter->data; out->message(out, "inject-modify-ticket", "Revoking", ticket_id); rc = set_ticket_state_attr(out, ticket_id, "granted", false, cib); CRM_ASSERT(rc == pcmk_rc_ok); } for (iter = injections->ticket_standby; iter != NULL; iter = iter->next) { const char *ticket_id = (const char *) iter->data; out->message(out, "inject-modify-ticket", "Standby", ticket_id); rc = set_ticket_state_attr(out, ticket_id, "standby", true, cib); CRM_ASSERT(rc == pcmk_rc_ok); } for (iter = injections->ticket_activate; iter != NULL; iter = iter->next) { const char *ticket_id = (const char *) iter->data; out->message(out, "inject-modify-ticket", "Activating", ticket_id); rc = set_ticket_state_attr(out, ticket_id, "standby", false, cib); CRM_ASSERT(rc == pcmk_rc_ok); } for (iter = injections->op_inject; iter != NULL; iter = iter->next) { inject_action(out, (const char *) iter->data, cib, data_set); } if (!out->is_quiet(out)) { out->end_list(out); } } void pcmk_free_injections(pcmk_injections_t *injections) { if (injections == NULL) { return; } g_list_free_full(injections->node_up, g_free); g_list_free_full(injections->node_down, g_free); g_list_free_full(injections->node_fail, g_free); g_list_free_full(injections->op_fail, g_free); g_list_free_full(injections->op_inject, g_free); g_list_free_full(injections->ticket_grant, g_free); g_list_free_full(injections->ticket_revoke, g_free); g_list_free_full(injections->ticket_standby, g_free); g_list_free_full(injections->ticket_activate, g_free); free(injections->quorum); free(injections->watchdog); free(injections); } diff --git a/lib/pacemaker/pcmk_sched_nodes.c b/lib/pacemaker/pcmk_sched_nodes.c index cf9e3a6182..5cfc28ecbf 100644 --- a/lib/pacemaker/pcmk_sched_nodes.c +++ b/lib/pacemaker/pcmk_sched_nodes.c @@ -1,430 +1,429 @@ /* * Copyright 2004-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include -#include // lrmd_event_data_t #include #include #include #include "libpacemaker_private.h" /*! * \internal * \brief Check whether a node is available to run resources * * \param[in] node Node to check * \param[in] consider_score If true, consider a negative score unavailable * \param[in] consider_guest If true, consider a guest node unavailable whose * resource will not be active * * \return true if node is online and not shutting down, unclean, or in standby * or maintenance mode, otherwise false */ bool pcmk__node_available(const pe_node_t *node, bool consider_score, bool consider_guest) { if ((node == NULL) || (node->details == NULL) || !node->details->online || node->details->shutdown || node->details->unclean || node->details->standby || node->details->maintenance) { return false; } if (consider_score && (node->weight < 0)) { return false; } // @TODO Go through all callers to see which should set consider_guest if (consider_guest && pe__is_guest_node(node)) { pe_resource_t *guest = node->details->remote_rsc->container; if (guest->fns->location(guest, NULL, FALSE) == NULL) { return false; } } return true; } /*! * \internal * \brief Copy a hash table of node objects * * \param[in] nodes Hash table to copy * * \return New copy of nodes (or NULL if nodes is NULL) */ GHashTable * pcmk__copy_node_table(GHashTable *nodes) { GHashTable *new_table = NULL; GHashTableIter iter; pe_node_t *node = NULL; if (nodes == NULL) { return NULL; } new_table = pcmk__strkey_table(NULL, free); g_hash_table_iter_init(&iter, nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) { pe_node_t *new_node = pe__copy_node(node); g_hash_table_insert(new_table, (gpointer) new_node->details->id, new_node); } return new_table; } /*! * \internal * \brief Free a table of node tables * * \param[in,out] data Table to free * * \note This is a \c GDestroyNotify wrapper for \c g_hash_table_destroy(). */ static void destroy_node_tables(gpointer data) { g_hash_table_destroy((GHashTable *) data); } /*! * \internal * \brief Recursively copy the node tables of a resource * * Build a hash table containing copies of the allowed nodes tables of \p rsc * and its entire tree of descendants. The key is the resource ID, and the value * is a copy of the resource's node table. * * \param[in] rsc Resource whose node table to copy * \param[in,out] copy Where to store the copied node tables * * \note \p *copy should be \c NULL for the top-level call. * \note The caller is responsible for freeing \p copy using * \c g_hash_table_destroy(). */ void pcmk__copy_node_tables(const pe_resource_t *rsc, GHashTable **copy) { CRM_ASSERT((rsc != NULL) && (copy != NULL)); if (*copy == NULL) { *copy = pcmk__strkey_table(NULL, destroy_node_tables); } g_hash_table_insert(*copy, rsc->id, pcmk__copy_node_table(rsc->allowed_nodes)); for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk__copy_node_tables((const pe_resource_t *) iter->data, copy); } } /*! * \internal * \brief Recursively restore the node tables of a resource from backup * * Given a hash table containing backup copies of the allowed nodes tables of * \p rsc and its entire tree of descendants, replace the resources' current * node tables with the backed-up copies. * * \param[in,out] rsc Resource whose node tables to restore * \param[in] backup Table of backup node tables (created by * \c pcmk__copy_node_tables()) * * \note This function frees the resources' current node tables. */ void pcmk__restore_node_tables(pe_resource_t *rsc, GHashTable *backup) { CRM_ASSERT((rsc != NULL) && (backup != NULL)); g_hash_table_destroy(rsc->allowed_nodes); // Copy to avoid danger with multiple restores rsc->allowed_nodes = g_hash_table_lookup(backup, rsc->id); rsc->allowed_nodes = pcmk__copy_node_table(rsc->allowed_nodes); for (GList *iter = rsc->children; iter != NULL; iter = iter->next) { pcmk__restore_node_tables((pe_resource_t *) iter->data, backup); } } /*! * \internal * \brief Copy a list of node objects * * \param[in] list List to copy * \param[in] reset Set copies' scores to 0 * * \return New list of shallow copies of nodes in original list */ GList * pcmk__copy_node_list(const GList *list, bool reset) { GList *result = NULL; for (const GList *iter = list; iter != NULL; iter = iter->next) { pe_node_t *new_node = NULL; pe_node_t *this_node = iter->data; new_node = pe__copy_node(this_node); if (reset) { new_node->weight = 0; } result = g_list_prepend(result, new_node); } return result; } /*! * \internal * \brief Compare two nodes for assignment preference * * Given two nodes, check which one is more preferred by assignment criteria * such as node score and utilization. * * \param[in] a First node to compare * \param[in] b Second node to compare * \param[in] data Node that resource being assigned is active on, if any * * \return -1 if \p a is preferred, +1 if \p b is preferred, or 0 if they are * equally preferred */ static gint compare_nodes(gconstpointer a, gconstpointer b, gpointer data) { const pe_node_t *node1 = (const pe_node_t *) a; const pe_node_t *node2 = (const pe_node_t *) b; const pe_node_t *active = (const pe_node_t *) data; int node1_score = -INFINITY; int node2_score = -INFINITY; int result = 0; if (a == NULL) { return 1; } if (b == NULL) { return -1; } // Compare node scores if (pcmk__node_available(node1, false, false)) { node1_score = node1->weight; } if (pcmk__node_available(node2, false, false)) { node2_score = node2->weight; } if (node1_score > node2_score) { crm_trace("%s (%d) > %s (%d) : score", pe__node_name(node1), node1_score, pe__node_name(node2), node2_score); return -1; } if (node1_score < node2_score) { crm_trace("%s (%d) < %s (%d) : score", pe__node_name(node1), node1_score, pe__node_name(node2), node2_score); return 1; } crm_trace("%s (%d) == %s (%d) : score", pe__node_name(node1), node1_score, pe__node_name(node2), node2_score); // If appropriate, compare node utilization if (pcmk__str_eq(node1->details->data_set->placement_strategy, "minimal", pcmk__str_casei)) { goto equal; } if (pcmk__str_eq(node1->details->data_set->placement_strategy, "balanced", pcmk__str_casei)) { result = pcmk__compare_node_capacities(node1, node2); if (result < 0) { crm_trace("%s > %s : capacity (%d)", pe__node_name(node1), pe__node_name(node2), result); return -1; } else if (result > 0) { crm_trace("%s < %s : capacity (%d)", pe__node_name(node1), pe__node_name(node2), result); return 1; } } // Compare number of resources already assigned to node if (node1->details->num_resources < node2->details->num_resources) { crm_trace("%s (%d) > %s (%d) : resources", pe__node_name(node1), node1->details->num_resources, pe__node_name(node2), node2->details->num_resources); return -1; } else if (node1->details->num_resources > node2->details->num_resources) { crm_trace("%s (%d) < %s (%d) : resources", pe__node_name(node1), node1->details->num_resources, pe__node_name(node2), node2->details->num_resources); return 1; } // Check whether one node is already running desired resource if (active != NULL) { if (pe__same_node(active, node1)) { crm_trace("%s (%d) > %s (%d) : active", pe__node_name(node1), node1->details->num_resources, pe__node_name(node2), node2->details->num_resources); return -1; } else if (pe__same_node(active, node2)) { crm_trace("%s (%d) < %s (%d) : active", pe__node_name(node1), node1->details->num_resources, pe__node_name(node2), node2->details->num_resources); return 1; } } // If all else is equal, prefer node with lowest-sorting name equal: crm_trace("%s = %s", pe__node_name(node1), pe__node_name(node2)); return strcmp(node1->details->uname, node2->details->uname); } /*! * \internal * \brief Sort a list of nodes by assigment preference * * \param[in,out] nodes Node list to sort * \param[in] active_node Node where resource being assigned is active * * \return New head of sorted list */ GList * pcmk__sort_nodes(GList *nodes, pe_node_t *active_node) { return g_list_sort_with_data(nodes, compare_nodes, active_node); } /*! * \internal * \brief Check whether any node is available to run resources * * \param[in] nodes Nodes to check * * \return true if any node in \p nodes is available to run resources, * otherwise false */ bool pcmk__any_node_available(GHashTable *nodes) { GHashTableIter iter; const pe_node_t *node = NULL; if (nodes == NULL) { return false; } g_hash_table_iter_init(&iter, nodes); while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) { if (pcmk__node_available(node, true, false)) { return true; } } return false; } /*! * \internal * \brief Apply node health values for all nodes in cluster * * \param[in,out] data_set Cluster working set */ void pcmk__apply_node_health(pe_working_set_t *data_set) { int base_health = 0; enum pcmk__health_strategy strategy; const char *strategy_str = pe_pref(data_set->config_hash, PCMK__OPT_NODE_HEALTH_STRATEGY); strategy = pcmk__parse_health_strategy(strategy_str); if (strategy == pcmk__health_strategy_none) { return; } crm_info("Applying node health strategy '%s'", strategy_str); // The progressive strategy can use a base health score if (strategy == pcmk__health_strategy_progressive) { base_health = pe__health_score(PCMK__OPT_NODE_HEALTH_BASE, data_set); } for (GList *iter = data_set->nodes; iter != NULL; iter = iter->next) { pe_node_t *node = (pe_node_t *) iter->data; int health = pe__sum_node_health_scores(node, base_health); // An overall health score of 0 has no effect if (health == 0) { continue; } crm_info("Overall system health of %s is %d", pe__node_name(node), health); // Use node health as a location score for each resource on the node for (GList *r = data_set->resources; r != NULL; r = r->next) { pe_resource_t *rsc = (pe_resource_t *) r->data; bool constrain = true; if (health < 0) { /* Negative health scores do not apply to resources with * allow-unhealthy-nodes=true. */ constrain = !crm_is_true(g_hash_table_lookup(rsc->meta, PCMK__META_ALLOW_UNHEALTHY_NODES)); } if (constrain) { pcmk__new_location(strategy_str, rsc, health, NULL, node); } else { pe_rsc_trace(rsc, "%s is immune from health ban on %s", rsc->id, pe__node_name(node)); } } } } /*! * \internal * \brief Check for a node in a resource's parent's allowed nodes * * \param[in] rsc Resource whose parent should be checked * \param[in] node Node to check for * * \return Equivalent of \p node from \p rsc's parent's allowed nodes if any, * otherwise NULL */ pe_node_t * pcmk__top_allowed_node(const pe_resource_t *rsc, const pe_node_t *node) { GHashTable *allowed_nodes = NULL; if ((rsc == NULL) || (node == NULL)) { return NULL; } else if (rsc->parent == NULL) { allowed_nodes = rsc->allowed_nodes; } else { allowed_nodes = rsc->parent->allowed_nodes; } return g_hash_table_lookup(allowed_nodes, node->details->id); }