diff --git a/include/crm/common/resources_internal.h b/include/crm/common/resources_internal.h index 261c4bb957..ed2aa32e78 100644 --- a/include/crm/common/resources_internal.h +++ b/include/crm/common/resources_internal.h @@ -1,469 +1,469 @@ /* * Copyright 2024-2025 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_RESOURCES_INTERNAL__H #define PCMK__CRM_COMMON_RESOURCES_INTERNAL__H #include // bool #include // uint32_t #include // gboolean, gpointer, guint, etc. #include // xmlNode #include // pcmk_resource_t #include // enum rsc_role_e #include // pcmk_node_t, etc. #ifdef __cplusplus extern "C" { #endif /*! * \internal * \brief Set resource flags * * \param[in,out] resource Resource to set flags for * \param[in] flags_to_set Group of enum pcmk_rsc_flags to set */ #define pcmk__set_rsc_flags(resource, flags_to_set) do { \ (resource)->flags = pcmk__set_flags_as(__func__, __LINE__, \ LOG_TRACE, "Resource", (resource)->id, (resource)->flags, \ (flags_to_set), #flags_to_set); \ } while (0) /*! * \internal * \brief Clear resource flags * * \param[in,out] resource Resource to clear flags for * \param[in] flags_to_clear Group of enum pcmk_rsc_flags to clear */ #define pcmk__clear_rsc_flags(resource, flags_to_clear) do { \ (resource)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ LOG_TRACE, "Resource", (resource)->id, (resource)->flags, \ (flags_to_clear), #flags_to_clear); \ } while (0) //! Resource variants supported by Pacemaker enum pcmk__rsc_variant { // Order matters: some code compares greater or lesser than pcmk__rsc_variant_unknown = -1, //!< Unknown resource variant pcmk__rsc_variant_primitive = 0, //!< Primitive resource pcmk__rsc_variant_group = 1, //!< Group resource pcmk__rsc_variant_clone = 2, //!< Clone resource pcmk__rsc_variant_bundle = 3, //!< Bundle resource }; //! How to recover a resource that is incorrectly active on multiple nodes enum pcmk__multiply_active { pcmk__multiply_active_restart, //!< Stop on all, start on desired pcmk__multiply_active_stop, //!< Stop on all and leave stopped pcmk__multiply_active_block, //!< Do nothing to resource pcmk__multiply_active_unexpected, //!< Stop unexpected instances }; //! Resource scheduling flags enum pcmk__rsc_flags { // No resource flags set (compare with equality rather than bit set) pcmk__no_rsc_flags = 0ULL, // Whether resource has been removed from the configuration pcmk__rsc_removed = (1ULL << 0), /* NOTE: sbd (at least as of 1.5.2) uses pe_rsc_managed which equates to * this value, so the value should not be changed */ // Whether resource is managed pcmk__rsc_managed = (1ULL << 1), // Whether resource is blocked from further action pcmk__rsc_blocked = (1ULL << 2), // Whether resource has been removed but was launched pcmk__rsc_removed_launched = (1ULL << 3), // Whether resource has clone notifications enabled pcmk__rsc_notify = (1ULL << 4), // Whether resource is not an anonymous clone instance pcmk__rsc_unique = (1ULL << 5), // Whether resource's class is "stonith" pcmk__rsc_fence_device = (1ULL << 6), // Whether resource can be promoted and demoted pcmk__rsc_promotable = (1ULL << 7), // Whether resource has not yet been assigned to a node pcmk__rsc_unassigned = (1ULL << 8), // Whether resource is in the process of being assigned to a node pcmk__rsc_assigning = (1ULL << 9), // Whether resource is in the process of modifying allowed node scores pcmk__rsc_updating_nodes = (1ULL << 10), // Whether resource is in the process of scheduling actions to restart pcmk__rsc_restarting = (1ULL << 11), // Whether resource must be stopped (instead of demoted) if it is failed pcmk__rsc_stop_if_failed = (1ULL << 12), // Whether a reload action has been scheduled for resource pcmk__rsc_reload = (1ULL << 13), // Whether resource is a remote connection allowed to run on a remote node pcmk__rsc_remote_nesting_allowed = (1ULL << 14), // Whether resource has \c PCMK_META_CRITICAL meta-attribute enabled pcmk__rsc_critical = (1ULL << 15), // Whether resource is considered failed pcmk__rsc_failed = (1ULL << 16), // Flag for non-scheduler code to use to detect recursion loops pcmk__rsc_detect_loop = (1ULL << 17), // Whether resource is a Pacemaker Remote connection pcmk__rsc_is_remote_connection = (1ULL << 18), // Whether resource has pending start action in history pcmk__rsc_start_pending = (1ULL << 19), // Whether resource is probed only on nodes marked exclusive pcmk__rsc_exclusive_probes = (1ULL << 20), /* * Whether resource is multiply active with recovery set to * \c PCMK_VALUE_STOP_UNEXPECTED */ pcmk__rsc_stop_unexpected = (1ULL << 22), // Whether resource is allowed to live-migrate pcmk__rsc_migratable = (1ULL << 23), // Whether resource has an ignorable failure pcmk__rsc_ignore_failure = (1ULL << 24), // Whether resource is an implicit container resource for a bundle replica pcmk__rsc_replica_container = (1ULL << 25), // Whether resource, its node, or entire cluster is in maintenance mode pcmk__rsc_maintenance = (1ULL << 26), // Whether resource can be started or promoted only on quorate nodes pcmk__rsc_needs_quorum = (1ULL << 28), // Whether resource requires fencing before recovery if on unclean node pcmk__rsc_needs_fencing = (1ULL << 29), // Whether resource can be started or promoted only on unfenced nodes pcmk__rsc_needs_unfencing = (1ULL << 30), }; // Where to look for a resource enum pcmk__rsc_node { pcmk__rsc_node_none = 0U, // Nowhere pcmk__rsc_node_assigned = (1U << 0), // Where resource is assigned pcmk__rsc_node_current = (1U << 1), // Where resource is running pcmk__rsc_node_pending = (1U << 2), // Where resource is pending }; //! Resource assignment methods (implementation defined by libpacemaker) typedef struct pcmk__assignment_methods pcmk__assignment_methods_t; //! Resource object methods typedef struct { /*! * \internal * \brief Parse variant-specific resource XML from CIB into struct members * * \param[in,out] rsc Partially unpacked resource * * \return \c true if resource was unpacked successfully, otherwise \c false */ bool (*unpack)(pcmk_resource_t *rsc); /*! * \internal * \brief Search for a resource ID in a resource and its children * * \param[in] rsc Search this resource and its children * \param[in] id Search for this resource ID * \param[in] on_node If not NULL, limit search to resources on this node * \param[in] flags Group of enum pe_find flags * * \return Resource that matches search criteria if any, otherwise NULL */ pcmk_resource_t *(*find_rsc)(pcmk_resource_t *rsc, const char *search, - const pcmk_node_t *node, int flags); + const pcmk_node_t *node, uint32_t flags); /*! * \internal * \brief Get value of a resource instance attribute * * \param[in,out] rsc Resource to check * \param[in] node Node to use to evaluate rules * \param[in] create Ignored * \param[in] name Name of instance attribute to check * \param[in,out] scheduler Scheduler data * * \return Value of requested attribute if available, otherwise NULL * \note The caller is responsible for freeing the result using free(). */ char *(*parameter)(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean create, const char *name, pcmk_scheduler_t *scheduler); /*! * \internal * \brief Check whether a resource is active * * \param[in] rsc Resource to check * \param[in] all If \p rsc is collective, all instances must be active * * \return TRUE if \p rsc is active, otherwise FALSE */ gboolean (*active)(pcmk_resource_t *rsc, gboolean all); /*! * \internal * \brief Get resource's current or assigned role * * \param[in] rsc Resource to check * \param[in] current If TRUE, check current role, otherwise assigned role * * \return Current or assigned role of \p rsc */ enum rsc_role_e (*state)(const pcmk_resource_t *rsc, gboolean current); /*! * \internal * \brief List nodes where a resource (or any of its children) is * * \param[in] rsc Resource to check * \param[out] list List to add result to * \param[in] target Which resource conditions to target (group of * enum pcmk__rsc_node flags) * * \return If list contains only one node, that node, otherwise NULL */ pcmk_node_t *(*location)(const pcmk_resource_t *rsc, GList **list, uint32_t target); /*! * \internal * \brief Free all memory used by a resource * * \param[in,out] rsc Resource to free */ void (*free)(pcmk_resource_t *rsc); /*! * \internal * \brief Increment cluster's instance counts for a resource * * Given a resource, increment its cluster's ninstances, disabled_resources, * and blocked_resources counts for the resource and its descendants. * * \param[in,out] rsc Resource to count */ void (*count)(pcmk_resource_t *rsc); /*! * \internal * \brief Check whether a given resource is in a list of resources * * \param[in] rsc Resource ID to check for * \param[in] only_rsc List of resource IDs to check * \param[in] check_parent If TRUE, check top ancestor as well * * \return TRUE if \p rsc, its top parent if requested, or '*' is in * \p only_rsc, otherwise FALSE */ gboolean (*is_filtered)(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent); /*! * \internal * \brief Find a node (and optionally count all) where resource is active * * \param[in] rsc Resource to check * \param[out] count_all If not NULL, set this to count of active nodes * \param[out] count_clean If not NULL, set this to count of clean nodes * * \return A node where the resource is active, preferring the source node * if the resource is involved in a partial migration, or a clean, * online node if the resource's \c PCMK_META_REQUIRES is * \c PCMK_VALUE_QUORUM or \c PCMK_VALUE_NOTHING, otherwise \c NULL. */ pcmk_node_t *(*active_node)(const pcmk_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean); /*! * \internal * \brief Get maximum resource instances per node * * \param[in] rsc Resource to check * * \return Maximum number of \p rsc instances that can be active on one node */ unsigned int (*max_per_node)(const pcmk_resource_t *rsc); } pcmk__rsc_methods_t; // Implementation of pcmk__resource_private_t struct pcmk__resource_private { enum pcmk__rsc_variant variant; // Resource variant void *variant_opaque; // Variant-specific data char *history_id; // Resource instance ID in history GHashTable *meta; // Resource meta-attributes GHashTable *utilization; // Resource utilization attributes int priority; // Priority relative other resources int promotion_priority; // Promotion priority on assigned node enum rsc_role_e orig_role; // Resource's role at start of transition enum rsc_role_e next_role; // Resource's role at end of transition int stickiness; // Extra preference for current node guint failure_expiration_ms; // Failures expire after this much time int ban_after_failures; // Ban from node after this many failures guint remote_reconnect_ms; // Retry interval for remote connections char *pending_action; // Pending action in history, if any const pcmk_node_t *pending_node;// Node on which pending_action is happening time_t lock_time; // When shutdown lock started const pcmk_node_t *lock_node; // Node that resource is shutdown-locked to GList *actions; // Actions scheduled for resource GList *children; // Resource's child resources, if any pcmk_resource_t *parent; // Resource's parent resource, if any pcmk_scheduler_t *scheduler; // Scheduler data containing resource // Resource configuration (possibly expanded from template) xmlNode *xml; // Original resource configuration, if using template xmlNode *orig_xml; // Configuration of resource operations (possibly expanded from template) xmlNode *ops_xml; /* * Resource parameters may have node-attribute-based rules, which means the * values can vary by node. This table has node names as keys and parameter * name/value tables as values. Use pe_rsc_params() to get the table for a * given node rather than use this directly. */ GHashTable *parameter_cache; /* A "launcher" is defined in one of these ways: * * - A Pacemaker Remote connection for a guest node or bundle node has its * launcher set to the resource that starts the guest or the bundle * replica's container. * * - If the user configures the PCMK__META_CONTAINER meta-attribute for this * resource, the launcher is set to that. * * If the launcher is a Pacemaker Remote connection resource, this * resource may run only on the node created by that connection. * * Otherwise, this resource will be colocated with and ordered after the * launcher, and failures of this resource will cause the launcher to be * recovered instead of this one. This is appropriate for monitoring-only * resources that represent a service launched by the other resource. */ pcmk_resource_t *launcher; // Resources launched by this one, if any (pcmk_resource_t *) GList *launched; // What to do if the resource is incorrectly active on multiple nodes enum pcmk__multiply_active multiply_active_policy; /* The assigned node (if not NULL) is the one where the resource *should* * be active by the end of the current scheduler transition. Only primitive * resources have an assigned node. This is a node copy (created by * pe__copy_node()) and so must be freed using pcmk__free_node_copy(). * * @TODO This should probably be part of the primitive variant data. */ pcmk_node_t *assigned_node; /* The active nodes are ones where the resource is (or might be, if * insufficient information is available to be sure) already active at the * start of the current scheduler transition. * * For primitive resources, there should be at most one, but could be more * if it is (incorrectly) multiply active. For collective resources, this * combines active nodes of all descendants. */ GList *active_nodes; /* The next two tables store node copies (created by pe__copy_node()), which * share some members with the original node objects and must be freed with * pcmk__free_node_copy(). */ // Nodes where resource has been probed (key is node ID, not name) GHashTable *probed_nodes; // Nodes where resource is allowed to run (key is node ID, not name) GHashTable *allowed_nodes; // The source node, if migrate_to completed but migrate_from has not pcmk_node_t *partial_migration_source; // The destination node, if migrate_to completed but migrate_from has not pcmk_node_t *partial_migration_target; // Source nodes where stop is needed after migrate_from and migrate_to GList *dangling_migration_sources; /* Pay special attention to whether you want to use with_this_colocations * and this_with_colocations directly, which include only colocations * explicitly involving this resource, or call libpacemaker's * pcmk__with_this_colocations() and pcmk__this_with_colocations() * functions, which may return relevant colocations involving the resource's * ancestors as well. */ // Colocations of other resources with this one GList *with_this_colocations; // Colocations of this resource with others GList *this_with_colocations; GList *location_constraints; // Location constraints for resource GList *ticket_constraints; // Ticket constraints for resource const pcmk__rsc_methods_t *fns; // Resource object methods const pcmk__assignment_methods_t *cmds; // Resource assignment methods }; void pcmk__free_resource(gpointer user_data); const char *pcmk__multiply_active_text(const pcmk_resource_t *rsc); /*! * \internal * \brief Get node where resource is currently active (if any) * * \param[in] rsc Resource to check * * \return Node that \p rsc is active on, if any, otherwise NULL */ static inline pcmk_node_t * pcmk__current_node(const pcmk_resource_t *rsc) { if (rsc == NULL) { return NULL; } return rsc->priv->fns->active_node(rsc, NULL, NULL); } #ifdef __cplusplus } #endif #endif // PCMK__CRM_COMMON_RESOURCES_INTERNAL__H diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h index 2d999f1db8..26785618b1 100644 --- a/include/crm/pengine/internal.h +++ b/include/crm/pengine/internal.h @@ -1,400 +1,400 @@ /* * Copyright 2004-2025 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_PENGINE_INTERNAL__H #define PCMK__CRM_PENGINE_INTERNAL__H #include -#include +#include // uint32_t #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif const char *pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts); bool pe__clone_is_ordered(const pcmk_resource_t *clone); int pe__set_clone_flag(pcmk_resource_t *clone, enum pcmk__clone_flags flag); bool pe__clone_flag_is_set(const pcmk_resource_t *clone, uint32_t flags); bool pe__group_flag_is_set(const pcmk_resource_t *group, uint32_t flags); pcmk_resource_t *pe__last_group_member(const pcmk_resource_t *group); const pcmk_resource_t *pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle); int pe__clone_max(const pcmk_resource_t *clone); int pe__clone_node_max(const pcmk_resource_t *clone); int pe__clone_promoted_max(const pcmk_resource_t *clone); int pe__clone_promoted_node_max(const pcmk_resource_t *clone); void pe__create_clone_notifications(pcmk_resource_t *clone); void pe__free_clone_notification_data(pcmk_resource_t *clone); void pe__create_clone_notif_pseudo_ops(pcmk_resource_t *clone, pcmk_action_t *start, pcmk_action_t *started, pcmk_action_t *stop, pcmk_action_t *stopped); pcmk_action_t *pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional, bool runnable); void pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting, bool any_demoting); bool pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node); char *native_parameter(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean create, const char *name, pcmk_scheduler_t *scheduler); pcmk_node_t *native_location(const pcmk_resource_t *rsc, GList **list, uint32_t target); void native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node, pcmk_scheduler_t *scheduler, gboolean failed); bool native_unpack(pcmk_resource_t *rsc); bool group_unpack(pcmk_resource_t *rsc); bool clone_unpack(pcmk_resource_t *rsc); bool pe__unpack_bundle(pcmk_resource_t *rsc); pcmk_resource_t *native_find_rsc(pcmk_resource_t *rsc, const char *id, - const pcmk_node_t *node, int flags); + const pcmk_node_t *node, uint32_t flags); gboolean native_active(pcmk_resource_t *rsc, gboolean all); gboolean group_active(pcmk_resource_t *rsc, gboolean all); gboolean clone_active(pcmk_resource_t *rsc, gboolean all); gboolean pe__bundle_active(pcmk_resource_t *rsc, gboolean all); gchar *pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, uint32_t show_opts, const char *target_role, bool show_nodes); int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name, ...) G_GNUC_NULL_TERMINATED; char *pe__node_display_name(pcmk_node_t *node, bool print_detail); // Clone notifications (pe_notif.c) void pe__order_notifs_after_fencing(const pcmk_action_t *action, pcmk_resource_t *rsc, pcmk_action_t *stonith_op); // Resource output methods int pe__clone_xml(pcmk__output_t *out, va_list args); int pe__clone_default(pcmk__output_t *out, va_list args); int pe__group_xml(pcmk__output_t *out, va_list args); int pe__group_default(pcmk__output_t *out, va_list args); int pe__bundle_xml(pcmk__output_t *out, va_list args); int pe__bundle_html(pcmk__output_t *out, va_list args); int pe__bundle_text(pcmk__output_t *out, va_list args); int pe__node_html(pcmk__output_t *out, va_list args); int pe__node_text(pcmk__output_t *out, va_list args); int pe__node_xml(pcmk__output_t *out, va_list args); int pe__resource_xml(pcmk__output_t *out, va_list args); int pe__resource_html(pcmk__output_t *out, va_list args); int pe__resource_text(pcmk__output_t *out, va_list args); void native_free(pcmk_resource_t *rsc); void group_free(pcmk_resource_t *rsc); void clone_free(pcmk_resource_t *rsc); void pe__free_bundle(pcmk_resource_t *rsc); enum rsc_role_e native_resource_state(const pcmk_resource_t *rsc, gboolean current); enum rsc_role_e group_resource_state(const pcmk_resource_t *rsc, gboolean current); enum rsc_role_e clone_resource_state(const pcmk_resource_t *rsc, gboolean current); enum rsc_role_e pe__bundle_resource_state(const pcmk_resource_t *rsc, gboolean current); void pe__count_common(pcmk_resource_t *rsc); void pe__count_bundle(pcmk_resource_t *rsc); void common_free(pcmk_resource_t *rsc); pcmk_node_t *pe__copy_node(const pcmk_node_t *this_node); /* Failure handling utilities (from failcounts.c) */ int pe_get_failcount(const pcmk_node_t *node, pcmk_resource_t *rsc, time_t *last_failure, uint32_t flags, const xmlNode *xml_op); pcmk_action_t *pe__clear_failcount(pcmk_resource_t *rsc, const pcmk_node_t *node, const char *reason, pcmk_scheduler_t *scheduler); /* Functions for finding/counting a resource's active nodes */ bool pe__count_active_node(const pcmk_resource_t *rsc, pcmk_node_t *node, pcmk_node_t **active, unsigned int *count_all, unsigned int *count_clean); pcmk_node_t *pe__find_active_requires(const pcmk_resource_t *rsc, unsigned int *count); /* Binary like operators for lists of nodes */ GHashTable *pe__node_list2table(const GList *list); pcmk_action_t *get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler); gboolean order_actions(pcmk_action_t *first, pcmk_action_t *then, uint32_t flags); void pe__show_node_scores_as(const char *file, const char *function, int line, bool to_log, const pcmk_resource_t *rsc, const char *comment, GHashTable *nodes, pcmk_scheduler_t *scheduler); #define pe__show_node_scores(level, rsc, text, nodes, scheduler) \ pe__show_node_scores_as(__FILE__, __func__, __LINE__, \ (level), (rsc), (text), (nodes), (scheduler)) GHashTable *pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, const char *action_name, guint interval_ms, const xmlNode *action_config); GHashTable *pcmk__unpack_action_rsc_params(const xmlNode *action_xml, GHashTable *node_attrs, pcmk_scheduler_t *data_set); xmlNode *pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, bool include_disabled); enum pcmk__requires pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name); enum pcmk__on_fail pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, const char *value); enum rsc_role_e pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name, enum pcmk__on_fail on_fail, GHashTable *meta); pcmk_action_t *custom_action(pcmk_resource_t *rsc, char *key, const char *task, const pcmk_node_t *on_node, gboolean optional, pcmk_scheduler_t *scheduler); #define delete_key(rsc) pcmk__op_key((rsc)->id, PCMK_ACTION_DELETE, 0) #define stop_key(rsc) pcmk__op_key((rsc)->id, PCMK_ACTION_STOP, 0) #define reload_key(rsc) pcmk__op_key((rsc)->id, PCMK_ACTION_RELOAD_AGENT, 0) #define start_key(rsc) pcmk__op_key((rsc)->id, PCMK_ACTION_START, 0) #define promote_key(rsc) pcmk__op_key((rsc)->id, PCMK_ACTION_PROMOTE, 0) #define demote_key(rsc) pcmk__op_key((rsc)->id, PCMK_ACTION_DEMOTE, 0) #define delete_action(rsc, node, optional) \ custom_action((rsc), delete_key(rsc), PCMK_ACTION_DELETE, \ (node), (optional), (rsc)->priv->scheduler) #define stop_action(rsc, node, optional) \ custom_action((rsc), stop_key(rsc), PCMK_ACTION_STOP, \ (node), (optional), (rsc)->priv->scheduler) #define start_action(rsc, node, optional) \ custom_action((rsc), start_key(rsc), PCMK_ACTION_START, \ (node), (optional), (rsc)->priv->scheduler) #define promote_action(rsc, node, optional) \ custom_action((rsc), promote_key(rsc), PCMK_ACTION_PROMOTE, \ (node), (optional), (rsc)->priv->scheduler) #define demote_action(rsc, node, optional) \ custom_action((rsc), demote_key(rsc), PCMK_ACTION_DEMOTE, \ (node), (optional), (rsc)->priv->scheduler) pcmk_action_t *find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node); enum pcmk__action_type get_complex_task(const pcmk_resource_t *rsc, const char *name); GList *find_actions(GList *input, const char *key, const pcmk_node_t *on_node); GList *find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node); GList *pe__resource_actions(const pcmk_resource_t *rsc, const pcmk_node_t *node, const char *task, bool require_node); void resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, const char *tag, pcmk_scheduler_t *scheduler); int pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b); extern gint sort_op_by_callid(gconstpointer a, gconstpointer b); gboolean get_target_role(const pcmk_resource_t *rsc, enum rsc_role_e *role); void pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why); extern void destroy_ticket(gpointer data); pcmk__ticket_t *ticket_new(const char *ticket_id, pcmk_scheduler_t *scheduler); // Resources for manipulating resource names const char *pe_base_name_end(const char *id); char *clone_strip(const char *last_rsc_id); char *clone_zero(const char *last_rsc_id); static inline bool pe_base_name_eq(const pcmk_resource_t *rsc, const char *id) { if (id && rsc && rsc->id) { // Number of characters in rsc->id before any clone suffix size_t base_len = pe_base_name_end(rsc->id) - rsc->id + 1; return (strlen(id) == base_len) && !strncmp(id, rsc->id, base_len); } return false; } int pe__target_rc_from_xml(const xmlNode *xml_op); gint pe__cmp_node_name(gconstpointer a, gconstpointer b); bool is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any); pcmk__op_digest_t *pe__calculate_digests(pcmk_resource_t *rsc, const char *task, guint *interval_ms, const pcmk_node_t *node, const xmlNode *xml_op, GHashTable *overrides, bool calc_secure, pcmk_scheduler_t *scheduler); void pe__free_digests(gpointer ptr); pcmk__op_digest_t *rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op, pcmk_node_t *node, pcmk_scheduler_t *scheduler); pcmk_action_t *pe_fence_op(pcmk_node_t *node, const char *op, bool optional, const char *reason, bool priority_delay, pcmk_scheduler_t *scheduler); void trigger_unfencing(pcmk_resource_t *rsc, pcmk_node_t *node, const char *reason, pcmk_action_t *dependency, pcmk_scheduler_t *scheduler); char *pe__action2reason(const pcmk_action_t *action, enum pcmk__action_flags flag); void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite); void pe__add_action_expected_result(pcmk_action_t *action, int expected_result); void pe__set_resource_flags_recursive(pcmk_resource_t *rsc, uint64_t flags); void pe__clear_resource_flags_recursive(pcmk_resource_t *rsc, uint64_t flags); void pe__clear_resource_flags_on_all(pcmk_scheduler_t *scheduler, uint64_t flag); int pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, unsigned int options); void pe_fence_node(pcmk_scheduler_t *scheduler, pcmk_node_t *node, const char *reason, bool priority_delay); pcmk_node_t *pe_create_node(const char *id, const char *uname, const char *type, int score, pcmk_scheduler_t *scheduler); int pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, unsigned int options); int pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, unsigned int options); GList *pe__bundle_containers(const pcmk_resource_t *bundle); int pe__bundle_max(const pcmk_resource_t *rsc); bool pe__node_is_bundle_instance(const pcmk_resource_t *bundle, const pcmk_node_t *node); pcmk_resource_t *pe__bundled_resource(const pcmk_resource_t *rsc); const pcmk_resource_t *pe__get_rsc_in_container(const pcmk_resource_t *instance); pcmk_resource_t *pe__first_container(const pcmk_resource_t *bundle); void pe__foreach_bundle_replica(pcmk_resource_t *bundle, bool (*fn)(pcmk__bundle_replica_t *, void *), void *user_data); void pe__foreach_const_bundle_replica(const pcmk_resource_t *bundle, bool (*fn)(const pcmk__bundle_replica_t *, void *), void *user_data); pcmk_resource_t *pe__find_bundle_replica(const pcmk_resource_t *bundle, const pcmk_node_t *node); bool pe__bundle_needs_remote_name(pcmk_resource_t *rsc); const char *pe__add_bundle_remote_name(pcmk_resource_t *rsc, xmlNode *xml, const char *field); bool pe__is_universal_clone(const pcmk_resource_t *rsc, const pcmk_scheduler_t *scheduler); bool pe__shutdown_requested(const pcmk_node_t *node); /*! * \internal * \brief Register xml formatting message functions. * * \param[in,out] out Output object to register messages with */ void pe__register_messages(pcmk__output_t *out); void pe__unpack_dataset_nvpairs(const xmlNode *xml_obj, const char *set_name, const pcmk_rule_input_t *rule_input, GHashTable *hash, const char *always_first, pcmk_scheduler_t *scheduler); bool pe__resource_is_disabled(const pcmk_resource_t *rsc); void pe__clear_resource_history(pcmk_resource_t *rsc, const pcmk_node_t *node); GList *pe__rscs_with_tag(pcmk_scheduler_t *scheduler, const char *tag_name); GList *pe__unames_with_tag(pcmk_scheduler_t *scheduler, const char *tag_name); bool pe__rsc_has_tag(pcmk_scheduler_t *scheduler, const char *rsc, const char *tag); bool pe__uname_has_tag(pcmk_scheduler_t *scheduler, const char *node, const char *tag); bool pe__rsc_running_on_only(const pcmk_resource_t *rsc, const pcmk_node_t *node); bool pe__rsc_running_on_any(pcmk_resource_t *rsc, GList *node_list); GList *pe__filter_rsc_list(GList *rscs, GList *filter); GList * pe__build_node_name_list(pcmk_scheduler_t *scheduler, const char *s); GList * pe__build_rsc_list(pcmk_scheduler_t *scheduler, const char *s); bool pcmk__rsc_filtered_by_node(pcmk_resource_t *rsc, GList *only_node); gboolean pe__bundle_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent); gboolean pe__clone_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent); gboolean pe__group_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent); gboolean pe__native_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent); xmlNode *pe__failed_probe_for_rsc(const pcmk_resource_t *rsc, const char *name); const char *pe__clone_child_id(const pcmk_resource_t *rsc); int pe__sum_node_health_scores(const pcmk_node_t *node, int base_health); int pe__node_health(pcmk_node_t *node); static inline enum pcmk__health_strategy pe__health_strategy(pcmk_scheduler_t *scheduler) { const char *strategy = pcmk__cluster_option(scheduler->priv->options, PCMK_OPT_NODE_HEALTH_STRATEGY); return pcmk__parse_health_strategy(strategy); } #ifdef __cplusplus } #endif #endif // PCMK__CRM_PENGINE_INTERNAL__H diff --git a/lib/pengine/native.c b/lib/pengine/native.c index 85b28ae46c..693ea7907f 100644 --- a/lib/pengine/native.c +++ b/lib/pengine/native.c @@ -1,1178 +1,1178 @@ /* * Copyright 2004-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include // bool, true, false -#include +#include // uint32_t #include #include #include #include #include #include /*! * \internal * \brief Check whether a resource is active on multiple nodes */ static bool is_multiply_active(const pcmk_resource_t *rsc) { unsigned int count = 0; if (pcmk__is_primitive(rsc)) { pe__find_active_requires(rsc, &count); } return count > 1; } static void native_priority_to_node(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean failed) { int priority = 0; const bool promoted = (rsc->priv->orig_role == pcmk_role_promoted); if ((rsc->priv->priority == 0) || failed) { return; } if (promoted) { // Promoted instance takes base priority + 1 priority = rsc->priv->priority + 1; } else { priority = rsc->priv->priority; } node->priv->priority += priority; pcmk__rsc_trace(rsc, "%s now has priority %d with %s'%s' (priority: %d%s)", pcmk__node_name(node), node->priv->priority, (promoted? "promoted " : ""), rsc->id, rsc->priv->priority, (promoted? " + 1" : "")); /* Priority of a resource running on a guest node is added to the cluster * node as well. */ if ((node->priv->remote != NULL) && (node->priv->remote->priv->launcher != NULL)) { const pcmk_resource_t *launcher = NULL; launcher = node->priv->remote->priv->launcher; for (GList *gIter = launcher->priv->active_nodes; gIter != NULL; gIter = gIter->next) { pcmk_node_t *a_node = gIter->data; a_node->priv->priority += priority; pcmk__rsc_trace(rsc, "%s now has priority %d with %s'%s' " "(priority: %d%s) from guest node %s", pcmk__node_name(a_node), a_node->priv->priority, (promoted? "promoted " : ""), rsc->id, rsc->priv->priority, (promoted? " + 1" : ""), pcmk__node_name(node)); } } } void native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node, pcmk_scheduler_t *scheduler, gboolean failed) { pcmk_resource_t *parent = rsc->priv->parent; CRM_CHECK(node != NULL, return); for (GList *gIter = rsc->priv->active_nodes; gIter != NULL; gIter = gIter->next) { pcmk_node_t *a_node = (pcmk_node_t *) gIter->data; if (pcmk__same_node(a_node, node)) { return; } } pcmk__rsc_trace(rsc, "Adding %s to %s %s", rsc->id, pcmk__node_name(node), pcmk_is_set(rsc->flags, pcmk__rsc_managed)? "" : "(unmanaged)"); rsc->priv->active_nodes = g_list_append(rsc->priv->active_nodes, node); if (pcmk__is_primitive(rsc)) { node->details->running_rsc = g_list_append(node->details->running_rsc, rsc); native_priority_to_node(rsc, node, failed); if (node->details->maintenance) { pcmk__clear_rsc_flags(rsc, pcmk__rsc_managed); pcmk__set_rsc_flags(rsc, pcmk__rsc_maintenance); } } if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) { pcmk_resource_t *p = parent; pcmk__rsc_info(rsc, "resource %s isn't managed", rsc->id); resource_location(rsc, node, PCMK_SCORE_INFINITY, "not_managed_default", scheduler); while(p && node->details->online) { /* add without the additional location constraint */ p->priv->active_nodes = g_list_append(p->priv->active_nodes, node); p = p->priv->parent; } return; } if (is_multiply_active(rsc)) { switch (rsc->priv->multiply_active_policy) { case pcmk__multiply_active_stop: { GHashTableIter gIter; pcmk_node_t *local_node = NULL; /* make sure it doesn't come up again */ if (rsc->priv->allowed_nodes != NULL) { g_hash_table_destroy(rsc->priv->allowed_nodes); } rsc->priv->allowed_nodes = pe__node_list2table(scheduler->nodes); g_hash_table_iter_init(&gIter, rsc->priv->allowed_nodes); while (g_hash_table_iter_next(&gIter, NULL, (void **)&local_node)) { local_node->assign->score = -PCMK_SCORE_INFINITY; } } break; case pcmk__multiply_active_block: pcmk__clear_rsc_flags(rsc, pcmk__rsc_managed); pcmk__set_rsc_flags(rsc, pcmk__rsc_blocked); /* If the resource belongs to a group or bundle configured with * PCMK_META_MULTIPLE_ACTIVE=PCMK_VALUE_BLOCK, block the entire * entity. */ if ((pcmk__is_group(parent) || pcmk__is_bundle(parent)) && (parent->priv->multiply_active_policy == pcmk__multiply_active_block)) { for (GList *gIter = parent->priv->children; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *child = gIter->data; pcmk__clear_rsc_flags(child, pcmk__rsc_managed); pcmk__set_rsc_flags(child, pcmk__rsc_blocked); } } break; // pcmk__multiply_active_restart, pcmk__multiply_active_unexpected default: /* The scheduler will do the right thing because the relevant * variables and flags are set when unpacking the history. */ break; } crm_debug("%s is active on multiple nodes including %s: %s", rsc->id, pcmk__node_name(node), pcmk__multiply_active_text(rsc)); } else { pcmk__rsc_trace(rsc, "Resource %s is active on %s", rsc->id, pcmk__node_name(node)); } if (parent != NULL) { native_add_running(parent, node, scheduler, FALSE); } } static void recursive_clear_unique(pcmk_resource_t *rsc, gpointer user_data) { pcmk__clear_rsc_flags(rsc, pcmk__rsc_unique); pcmk__insert_meta(rsc->priv, PCMK_META_GLOBALLY_UNIQUE, PCMK_VALUE_FALSE); g_list_foreach(rsc->priv->children, (GFunc) recursive_clear_unique, NULL); } bool native_unpack(pcmk_resource_t *rsc) { pcmk_resource_t *parent = uber_parent(rsc); const char *standard = crm_element_value(rsc->priv->xml, PCMK_XA_CLASS); uint32_t ra_caps = pcmk_get_ra_caps(standard); pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id); // Only some agent standards support unique and promotable clones if (!pcmk_is_set(ra_caps, pcmk_ra_cap_unique) && pcmk_is_set(rsc->flags, pcmk__rsc_unique) && pcmk__is_clone(parent)) { /* @COMPAT We should probably reject this situation as an error (as we * do for promotable below) rather than warn and convert, but that would * be a backward-incompatible change that we should probably do with a * transform at a schema major version bump. */ pe__force_anon(standard, parent, rsc->id, rsc->priv->scheduler); /* Clear PCMK_META_GLOBALLY_UNIQUE on the parent and all its descendants * unpacked so far (clearing the parent should make any future children * unpacking correct). We have to clear this resource explicitly because * it isn't hooked into the parent's children yet. */ recursive_clear_unique(parent, NULL); recursive_clear_unique(rsc, NULL); } if (!pcmk_is_set(ra_caps, pcmk_ra_cap_promotable) && pcmk_is_set(parent->flags, pcmk__rsc_promotable)) { pcmk__config_err("Resource %s is of type %s and therefore " "cannot be used as a promotable clone resource", rsc->id, standard); return FALSE; } return TRUE; } static bool rsc_is_on_node(pcmk_resource_t *rsc, const pcmk_node_t *node, int flags) { pcmk__rsc_trace(rsc, "Checking whether %s is on %s", rsc->id, pcmk__node_name(node)); if (pcmk_is_set(flags, pcmk_rsc_match_current_node) && (rsc->priv->active_nodes != NULL)) { for (GList *iter = rsc->priv->active_nodes; iter != NULL; iter = iter->next) { if (pcmk__same_node((pcmk_node_t *) iter->data, node)) { return true; } } } else if (!pcmk_is_set(flags, pcmk_rsc_match_current_node) && (rsc->priv->assigned_node != NULL) && pcmk__same_node(rsc->priv->assigned_node, node)) { return true; } return false; } pcmk_resource_t * native_find_rsc(pcmk_resource_t *rsc, const char *id, - const pcmk_node_t *on_node, int flags) + const pcmk_node_t *on_node, uint32_t flags) { bool match = false; pcmk_resource_t *result = NULL; CRM_CHECK(id && rsc && rsc->id, return NULL); if (pcmk_is_set(flags, pcmk_rsc_match_clone_only)) { const char *rid = pcmk__xe_id(rsc->priv->xml); if (!pcmk__is_clone(pe__const_top_resource(rsc, false))) { match = false; } else if (!strcmp(id, rsc->id) || pcmk__str_eq(id, rid, pcmk__str_none)) { match = true; } } else if (!strcmp(id, rsc->id)) { match = true; } else if (pcmk_is_set(flags, pcmk_rsc_match_history) && pcmk__str_eq(rsc->priv->history_id, id, pcmk__str_none)) { match = true; } else if (pcmk_is_set(flags, pcmk_rsc_match_basename) || (pcmk_is_set(flags, pcmk_rsc_match_anon_basename) && !pcmk_is_set(rsc->flags, pcmk__rsc_unique))) { match = pe_base_name_eq(rsc, id); } if (match && on_node) { if (!rsc_is_on_node(rsc, on_node, flags)) { match = false; } } if (match) { return rsc; } for (GList *gIter = rsc->priv->children; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *child = (pcmk_resource_t *) gIter->data; result = rsc->priv->fns->find_rsc(child, id, on_node, flags); if (result) { return result; } } return NULL; } // create is ignored char * native_parameter(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean create, const char *name, pcmk_scheduler_t *scheduler) { const char *value = NULL; GHashTable *params = NULL; CRM_CHECK(rsc != NULL, return NULL); CRM_CHECK(name != NULL && strlen(name) != 0, return NULL); pcmk__rsc_trace(rsc, "Looking up %s in %s", name, rsc->id); params = pe_rsc_params(rsc, node, scheduler); value = g_hash_table_lookup(params, name); if (value == NULL) { /* try meta attributes instead */ value = g_hash_table_lookup(rsc->priv->meta, name); } return pcmk__str_copy(value); } gboolean native_active(pcmk_resource_t * rsc, gboolean all) { for (GList *gIter = rsc->priv->active_nodes; gIter != NULL; gIter = gIter->next) { pcmk_node_t *a_node = (pcmk_node_t *) gIter->data; if (a_node->details->unclean) { pcmk__rsc_trace(rsc, "Resource %s: %s is unclean", rsc->id, pcmk__node_name(a_node)); return TRUE; } else if (!a_node->details->online && pcmk_is_set(rsc->flags, pcmk__rsc_managed)) { pcmk__rsc_trace(rsc, "Resource %s: %s is offline", rsc->id, pcmk__node_name(a_node)); } else { pcmk__rsc_trace(rsc, "Resource %s active on %s", rsc->id, pcmk__node_name(a_node)); return TRUE; } } return FALSE; } struct print_data_s { long options; void *print_data; }; static const char * native_pending_state(const pcmk_resource_t *rsc) { const char *pending_state = NULL; if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_START, pcmk__str_none)) { pending_state = "Starting"; } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_STOP, pcmk__str_none)) { pending_state = "Stopping"; } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MIGRATE_TO, pcmk__str_none)) { pending_state = "Migrating"; } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MIGRATE_FROM, pcmk__str_none)) { /* Work might be done in here. */ pending_state = "Migrating"; } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_PROMOTE, pcmk__str_none)) { pending_state = "Promoting"; } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_DEMOTE, pcmk__str_none)) { pending_state = "Demoting"; } return pending_state; } static const char * native_pending_action(const pcmk_resource_t *rsc) { const char *pending_action = NULL; if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MONITOR, pcmk__str_none)) { pending_action = "Monitoring"; /* Pending probes are not printed, even if pending * operations are requested. If someone ever requests that * behavior, uncomment this and the corresponding part of * unpack.c:unpack_rsc_op(). */ #if 0 } else if (pcmk__str_eq(rsc->private->pending_action, "probe", pcmk__str_none)) { pending_action = "Checking"; #endif } return pending_action; } static enum rsc_role_e native_displayable_role(const pcmk_resource_t *rsc) { enum rsc_role_e role = rsc->priv->orig_role; if ((role == pcmk_role_started) && pcmk_is_set(pe__const_top_resource(rsc, false)->flags, pcmk__rsc_promotable)) { role = pcmk_role_unpromoted; } return role; } static const char * native_displayable_state(const pcmk_resource_t *rsc, bool print_pending) { const char *rsc_state = NULL; if (print_pending) { rsc_state = native_pending_state(rsc); } if (rsc_state == NULL) { rsc_state = pcmk_role_text(native_displayable_role(rsc)); } return rsc_state; } // Append a flag to resource description string's flags list static bool add_output_flag(GString *s, const char *flag_desc, bool have_flags) { g_string_append(s, (have_flags? ", " : " (")); g_string_append(s, flag_desc); return true; } // Append a node name to resource description string's node list static bool add_output_node(GString *s, const char *node, bool have_nodes) { g_string_append(s, (have_nodes? " " : " [ ")); g_string_append(s, node); return true; } /*! * \internal * \brief Create a string description of a resource * * \param[in] rsc Resource to describe * \param[in] name Desired identifier for the resource * \param[in] node If not NULL, node that resource is "on" * \param[in] show_opts Bitmask of pcmk_show_opt_e. * \param[in] target_role Resource's target role * \param[in] show_nodes Whether to display nodes when multiply active * * \return Newly allocated string description of resource * \note Caller must free the result with g_free(). */ gchar * pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, uint32_t show_opts, const char *target_role, bool show_nodes) { const char *class = crm_element_value(rsc->priv->xml, PCMK_XA_CLASS); const char *provider = NULL; const char *kind = crm_element_value(rsc->priv->xml, PCMK_XA_TYPE); GString *outstr = NULL; bool have_flags = false; if (!pcmk__is_primitive(rsc)) { return NULL; } CRM_CHECK(name != NULL, name = "unknown"); CRM_CHECK(kind != NULL, kind = "unknown"); CRM_CHECK(class != NULL, class = "unknown"); if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) { provider = crm_element_value(rsc->priv->xml, PCMK_XA_PROVIDER); } if ((node == NULL) && (rsc->priv->lock_node != NULL)) { node = rsc->priv->lock_node; } if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only) || pcmk__list_of_multiple(rsc->priv->active_nodes)) { node = NULL; } outstr = g_string_sized_new(128); // Resource name and agent pcmk__g_strcat(outstr, name, "\t(", class, ((provider == NULL)? "" : ":"), pcmk__s(provider, ""), ":", kind, "):\t", NULL); // State on node if (pcmk_is_set(rsc->flags, pcmk__rsc_removed)) { g_string_append(outstr, " ORPHANED"); } if (pcmk_is_set(rsc->flags, pcmk__rsc_failed)) { enum rsc_role_e role = native_displayable_role(rsc); g_string_append(outstr, " FAILED"); if (role > pcmk_role_unpromoted) { pcmk__add_word(&outstr, 0, pcmk_role_text(role)); } } else { bool show_pending = pcmk_is_set(show_opts, pcmk_show_pending); pcmk__add_word(&outstr, 0, native_displayable_state(rsc, show_pending)); } if (node) { pcmk__add_word(&outstr, 0, pcmk__node_name(node)); } // Failed probe operation if (native_displayable_role(rsc) == pcmk_role_stopped) { xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node ? node->priv->name : NULL); if (probe_op != NULL) { int rc; pcmk__scan_min_int(crm_element_value(probe_op, PCMK__XA_RC_CODE), &rc, 0); pcmk__g_strcat(outstr, " (", crm_exit_str(rc), ") ", NULL); } } // Flags, as: ( [...]) if (node && !(node->details->online) && node->details->unclean) { have_flags = add_output_flag(outstr, "UNCLEAN", have_flags); } if ((node != NULL) && pcmk__same_node(node, rsc->priv->lock_node)) { have_flags = add_output_flag(outstr, "LOCKED", have_flags); } if (pcmk_is_set(show_opts, pcmk_show_pending)) { const char *pending_action = native_pending_action(rsc); if (pending_action != NULL) { have_flags = add_output_flag(outstr, pending_action, have_flags); } } if (target_role != NULL) { switch (pcmk_parse_role(target_role)) { case pcmk_role_unknown: pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE " %s for resource %s", target_role, rsc->id); break; case pcmk_role_stopped: have_flags = add_output_flag(outstr, "disabled", have_flags); break; case pcmk_role_unpromoted: if (pcmk_is_set(pe__const_top_resource(rsc, false)->flags, pcmk__rsc_promotable)) { have_flags = add_output_flag(outstr, PCMK_META_TARGET_ROLE ":", have_flags); g_string_append(outstr, target_role); } break; default: /* Only show target role if it limits our abilities (i.e. ignore * Started, as it is the default anyways, and doesn't prevent * the resource from becoming promoted). */ break; } } // Blocked or maintenance implies unmanaged if (pcmk_any_flags_set(rsc->flags, pcmk__rsc_blocked|pcmk__rsc_maintenance)) { if (pcmk_is_set(rsc->flags, pcmk__rsc_blocked)) { have_flags = add_output_flag(outstr, "blocked", have_flags); } else if (pcmk_is_set(rsc->flags, pcmk__rsc_maintenance)) { have_flags = add_output_flag(outstr, "maintenance", have_flags); } } else if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) { have_flags = add_output_flag(outstr, "unmanaged", have_flags); } if (pcmk_is_set(rsc->flags, pcmk__rsc_ignore_failure)) { have_flags = add_output_flag(outstr, "failure ignored", have_flags); } if (have_flags) { g_string_append_c(outstr, ')'); } // User-supplied description if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description) || pcmk__list_of_multiple(rsc->priv->active_nodes)) { const char *desc = crm_element_value(rsc->priv->xml, PCMK_XA_DESCRIPTION); if (desc) { g_string_append(outstr, " ("); g_string_append(outstr, desc); g_string_append(outstr, ")"); } } if (show_nodes && !pcmk_is_set(show_opts, pcmk_show_rsc_only) && pcmk__list_of_multiple(rsc->priv->active_nodes)) { bool have_nodes = false; for (GList *iter = rsc->priv->active_nodes; iter != NULL; iter = iter->next) { pcmk_node_t *n = (pcmk_node_t *) iter->data; have_nodes = add_output_node(outstr, n->priv->name, have_nodes); } if (have_nodes) { g_string_append(outstr, " ]"); } } return g_string_free(outstr, FALSE); } int pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, uint32_t show_opts) { const char *kind = crm_element_value(rsc->priv->xml, PCMK_XA_TYPE); const char *target_role = NULL; const char *cl = NULL; xmlNode *child = NULL; gchar *content = NULL; pcmk__assert((kind != NULL) && pcmk__is_primitive(rsc)); if (crm_is_true(g_hash_table_lookup(rsc->priv->meta, PCMK__META_INTERNAL_RSC)) && !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) { crm_trace("skipping print of internal resource %s", rsc->id); return pcmk_rc_no_output; } target_role = g_hash_table_lookup(rsc->priv->meta, PCMK_META_TARGET_ROLE); if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) { cl = PCMK__VALUE_RSC_MANAGED; } else if (pcmk_is_set(rsc->flags, pcmk__rsc_failed)) { cl = PCMK__VALUE_RSC_FAILED; } else if (pcmk__is_primitive(rsc) && (rsc->priv->active_nodes == NULL)) { cl = PCMK__VALUE_RSC_FAILED; } else if (pcmk__list_of_multiple(rsc->priv->active_nodes)) { cl = PCMK__VALUE_RSC_MULTIPLE; } else if (pcmk_is_set(rsc->flags, pcmk__rsc_ignore_failure)) { cl = PCMK__VALUE_RSC_FAILURE_IGNORED; } else { cl = PCMK__VALUE_RSC_OK; } child = pcmk__output_create_html_node(out, "li", NULL, NULL, NULL); child = pcmk__html_create(child, PCMK__XE_SPAN, NULL, cl); content = pcmk__native_output_string(rsc, name, node, show_opts, target_role, true); pcmk__xe_set_content(child, "%s", content); g_free(content); return pcmk_rc_ok; } int pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc, const char *name, const pcmk_node_t *node, uint32_t show_opts) { const char *target_role = NULL; pcmk__assert(pcmk__is_primitive(rsc)); if (crm_is_true(g_hash_table_lookup(rsc->priv->meta, PCMK__META_INTERNAL_RSC)) && !pcmk_is_set(show_opts, pcmk_show_implicit_rscs)) { crm_trace("skipping print of internal resource %s", rsc->id); return pcmk_rc_no_output; } target_role = g_hash_table_lookup(rsc->priv->meta, PCMK_META_TARGET_ROLE); { gchar *s = pcmk__native_output_string(rsc, name, node, show_opts, target_role, true); out->list_item(out, NULL, "%s", s); g_free(s); } return pcmk_rc_ok; } PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *", "GList *") int pe__resource_xml(pcmk__output_t *out, va_list args) { uint32_t show_opts = va_arg(args, uint32_t); pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); GList *only_node G_GNUC_UNUSED = va_arg(args, GList *); GList *only_rsc = va_arg(args, GList *); int rc = pcmk_rc_no_output; bool print_pending = pcmk_is_set(show_opts, pcmk_show_pending); const char *class = crm_element_value(rsc->priv->xml, PCMK_XA_CLASS); const char *prov = crm_element_value(rsc->priv->xml, PCMK_XA_PROVIDER); char ra_name[LINE_MAX]; const char *rsc_state = native_displayable_state(rsc, print_pending); const char *target_role = NULL; const char *active = pcmk__btoa(rsc->priv->fns->active(rsc, TRUE)); const char *orphaned = pcmk__flag_text(rsc->flags, pcmk__rsc_removed); const char *blocked = pcmk__flag_text(rsc->flags, pcmk__rsc_blocked); const char *maintenance = pcmk__flag_text(rsc->flags, pcmk__rsc_maintenance); const char *managed = pcmk__flag_text(rsc->flags, pcmk__rsc_managed); const char *failed = pcmk__flag_text(rsc->flags, pcmk__rsc_failed); const char *ignored = pcmk__flag_text(rsc->flags, pcmk__rsc_ignore_failure); char *nodes_running_on = NULL; const char *pending = print_pending? native_pending_action(rsc) : NULL; const char *locked_to = NULL; const char *desc = pe__resource_description(rsc, show_opts); pcmk__assert(pcmk__is_primitive(rsc)); if (rsc->priv->fns->is_filtered(rsc, only_rsc, TRUE)) { return pcmk_rc_no_output; } // Resource information snprintf(ra_name, LINE_MAX, "%s%s%s:%s", class, ((prov == NULL)? "" : ":"), ((prov == NULL)? "" : prov), crm_element_value(rsc->priv->xml, PCMK_XA_TYPE)); target_role = g_hash_table_lookup(rsc->priv->meta, PCMK_META_TARGET_ROLE); nodes_running_on = pcmk__itoa(g_list_length(rsc->priv->active_nodes)); if (rsc->priv->lock_node != NULL) { locked_to = rsc->priv->lock_node->priv->name; } rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_RESOURCE, PCMK_XA_ID, rsc_printable_id(rsc), PCMK_XA_RESOURCE_AGENT, ra_name, PCMK_XA_ROLE, rsc_state, PCMK_XA_TARGET_ROLE, target_role, PCMK_XA_ACTIVE, active, PCMK_XA_ORPHANED, orphaned, PCMK_XA_BLOCKED, blocked, PCMK_XA_MAINTENANCE, maintenance, PCMK_XA_MANAGED, managed, PCMK_XA_FAILED, failed, PCMK_XA_FAILURE_IGNORED, ignored, PCMK_XA_NODES_RUNNING_ON, nodes_running_on, PCMK_XA_PENDING, pending, PCMK_XA_LOCKED_TO, locked_to, PCMK_XA_DESCRIPTION, desc, NULL); free(nodes_running_on); pcmk__assert(rc == pcmk_rc_ok); for (GList *gIter = rsc->priv->active_nodes; gIter != NULL; gIter = gIter->next) { pcmk_node_t *node = (pcmk_node_t *) gIter->data; const char *cached = pcmk__btoa(node->details->online); rc = pe__name_and_nvpairs_xml(out, false, PCMK_XE_NODE, PCMK_XA_NAME, node->priv->name, PCMK_XA_ID, node->priv->id, PCMK_XA_CACHED, cached, NULL); pcmk__assert(rc == pcmk_rc_ok); } pcmk__output_xml_pop_parent(out); return rc; } PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *", "GList *") int pe__resource_html(pcmk__output_t *out, va_list args) { uint32_t show_opts = va_arg(args, uint32_t); pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); GList *only_node G_GNUC_UNUSED = va_arg(args, GList *); GList *only_rsc = va_arg(args, GList *); const pcmk_node_t *node = pcmk__current_node(rsc); if (rsc->priv->fns->is_filtered(rsc, only_rsc, TRUE)) { return pcmk_rc_no_output; } pcmk__assert(pcmk__is_primitive(rsc)); if (node == NULL) { // This is set only if a non-probe action is pending on this node node = rsc->priv->pending_node; } return pe__common_output_html(out, rsc, rsc_printable_id(rsc), node, show_opts); } PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *", "GList *") int pe__resource_text(pcmk__output_t *out, va_list args) { uint32_t show_opts = va_arg(args, uint32_t); pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *); GList *only_node G_GNUC_UNUSED = va_arg(args, GList *); GList *only_rsc = va_arg(args, GList *); const pcmk_node_t *node = pcmk__current_node(rsc); pcmk__assert(pcmk__is_primitive(rsc)); if (rsc->priv->fns->is_filtered(rsc, only_rsc, TRUE)) { return pcmk_rc_no_output; } if (node == NULL) { // This is set only if a non-probe action is pending on this node node = rsc->priv->pending_node; } return pe__common_output_text(out, rsc, rsc_printable_id(rsc), node, show_opts); } void native_free(pcmk_resource_t * rsc) { pcmk__rsc_trace(rsc, "Freeing resource action list (not the data)"); common_free(rsc); } enum rsc_role_e native_resource_state(const pcmk_resource_t * rsc, gboolean current) { enum rsc_role_e role = rsc->priv->next_role; if (current) { role = rsc->priv->orig_role; } pcmk__rsc_trace(rsc, "%s state: %s", rsc->id, pcmk_role_text(role)); return role; } /*! * \internal * \brief List nodes where a resource (or any of its children) is * * \param[in] rsc Resource to check * \param[out] list List to add result to * \param[in] target Which resource conditions to target (group of * enum pcmk__rsc_node flags) * * \return If list contains only one node, that node, or NULL otherwise */ pcmk_node_t * native_location(const pcmk_resource_t *rsc, GList **list, uint32_t target) { pcmk_node_t *one = NULL; GList *result = NULL; if (rsc->priv->children != NULL) { for (GList *gIter = rsc->priv->children; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *child = (pcmk_resource_t *) gIter->data; child->priv->fns->location(child, &result, target); } } else { if (pcmk_is_set(target, pcmk__rsc_node_current)) { result = g_list_copy(rsc->priv->active_nodes); } if (pcmk_is_set(target, pcmk__rsc_node_pending) && (rsc->priv->pending_node != NULL) && !pe_find_node_id(result, rsc->priv->pending_node->priv->id)) { result = g_list_append(result, (gpointer) rsc->priv->pending_node); } if (pcmk_is_set(target, pcmk__rsc_node_assigned) && (rsc->priv->assigned_node != NULL)) { result = g_list_append(result, rsc->priv->assigned_node); } } if (result && (result->next == NULL)) { one = result->data; } if (list) { GList *gIter = result; for (; gIter != NULL; gIter = gIter->next) { pcmk_node_t *node = (pcmk_node_t *) gIter->data; if ((*list == NULL) || (pe_find_node_id(*list, node->priv->id) == NULL)) { *list = g_list_append(*list, node); } } } g_list_free(result); return one; } static void get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_table) { GList *gIter = rsc_list; for (; gIter != NULL; gIter = gIter->next) { pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data; const char *class = crm_element_value(rsc->priv->xml, PCMK_XA_CLASS); const char *kind = crm_element_value(rsc->priv->xml, PCMK_XA_TYPE); int offset = 0; char buffer[LINE_MAX]; int *rsc_counter = NULL; int *active_counter = NULL; if (!pcmk__is_primitive(rsc)) { continue; } offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", class); if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) { const char *prov = crm_element_value(rsc->priv->xml, PCMK_XA_PROVIDER); if (prov != NULL) { offset += snprintf(buffer + offset, LINE_MAX - offset, ":%s", prov); } } offset += snprintf(buffer + offset, LINE_MAX - offset, ":%s", kind); CRM_LOG_ASSERT(offset > 0); if (rsc_table) { rsc_counter = g_hash_table_lookup(rsc_table, buffer); if (rsc_counter == NULL) { rsc_counter = pcmk__assert_alloc(1, sizeof(int)); *rsc_counter = 0; g_hash_table_insert(rsc_table, strdup(buffer), rsc_counter); } (*rsc_counter)++; } if (active_table) { for (GList *gIter2 = rsc->priv->active_nodes; gIter2 != NULL; gIter2 = gIter2->next) { pcmk_node_t *node = (pcmk_node_t *) gIter2->data; GHashTable *node_table = NULL; if (node->details->unclean == FALSE && node->details->online == FALSE && pcmk_is_set(rsc->flags, pcmk__rsc_managed)) { continue; } node_table = g_hash_table_lookup(active_table, node->priv->name); if (node_table == NULL) { node_table = pcmk__strkey_table(free, free); g_hash_table_insert(active_table, strdup(node->priv->name), node_table); } active_counter = g_hash_table_lookup(node_table, buffer); if (active_counter == NULL) { active_counter = pcmk__assert_alloc(1, sizeof(int)); *active_counter = 0; g_hash_table_insert(node_table, strdup(buffer), active_counter); } (*active_counter)++; } } } } static void destroy_node_table(gpointer data) { GHashTable *node_table = data; if (node_table) { g_hash_table_destroy(node_table); } } int pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, uint32_t show_opts) { GHashTable *rsc_table = pcmk__strkey_table(free, free); GHashTable *active_table = pcmk__strkey_table(free, destroy_node_table); GList *sorted_rscs; int rc = pcmk_rc_no_output; get_rscs_brief(rsc_list, rsc_table, active_table); /* Make a list of the rsc_table keys so that it can be sorted. This is to make sure * output order stays consistent between systems. */ sorted_rscs = g_hash_table_get_keys(rsc_table); sorted_rscs = g_list_sort(sorted_rscs, (GCompareFunc) strcmp); for (GList *gIter = sorted_rscs; gIter; gIter = gIter->next) { char *type = (char *) gIter->data; int *rsc_counter = g_hash_table_lookup(rsc_table, type); GList *sorted_nodes = NULL; int active_counter_all = 0; /* Also make a list of the active_table keys so it can be sorted. If there's * more than one instance of a type of resource running, we need the nodes to * be sorted to make sure output order stays consistent between systems. */ sorted_nodes = g_hash_table_get_keys(active_table); sorted_nodes = g_list_sort(sorted_nodes, (GCompareFunc) pcmk__numeric_strcasecmp); for (GList *gIter2 = sorted_nodes; gIter2; gIter2 = gIter2->next) { char *node_name = (char *) gIter2->data; GHashTable *node_table = g_hash_table_lookup(active_table, node_name); int *active_counter = NULL; if (node_table == NULL) { continue; } active_counter = g_hash_table_lookup(node_table, type); if (active_counter == NULL || *active_counter == 0) { continue; } else { active_counter_all += *active_counter; } if (pcmk_is_set(show_opts, pcmk_show_rsc_only)) { node_name = NULL; } if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) { out->list_item(out, NULL, "%d/%d\t(%s):\tActive %s", *active_counter, rsc_counter ? *rsc_counter : 0, type, (*active_counter > 0) && node_name ? node_name : ""); } else { out->list_item(out, NULL, "%d\t(%s):\tActive %s", *active_counter, type, (*active_counter > 0) && node_name ? node_name : ""); } rc = pcmk_rc_ok; } if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs) && active_counter_all == 0) { out->list_item(out, NULL, "%d/%d\t(%s):\tActive", active_counter_all, rsc_counter ? *rsc_counter : 0, type); rc = pcmk_rc_ok; } if (sorted_nodes) { g_list_free(sorted_nodes); } } if (rsc_table) { g_hash_table_destroy(rsc_table); rsc_table = NULL; } if (active_table) { g_hash_table_destroy(active_table); active_table = NULL; } if (sorted_rscs) { g_list_free(sorted_rscs); } return rc; } gboolean pe__native_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc, gboolean check_parent) { if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) || pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) { return FALSE; } else if (check_parent && (rsc->priv->parent != NULL)) { const pcmk_resource_t *up = pe__const_top_resource(rsc, true); return up->priv->fns->is_filtered(up, only_rsc, FALSE); } return TRUE; } /*! * \internal * \brief Get maximum primitive resource instances per node * * \param[in] rsc Primitive resource to check * * \return Maximum number of \p rsc instances that can be active on one node */ unsigned int pe__primitive_max_per_node(const pcmk_resource_t *rsc) { pcmk__assert(pcmk__is_primitive(rsc)); return 1U; } diff --git a/lib/pengine/status.c b/lib/pengine/status.c index 22f75157b0..4b397c2b96 100644 --- a/lib/pengine/status.c +++ b/lib/pengine/status.c @@ -1,354 +1,355 @@ /* * Copyright 2004-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include +#include // uint32_t #include #include #include #include #include #include #include #define XPATH_DEPRECATED_RULES \ "//" PCMK_XE_OP_DEFAULTS "//" PCMK_XE_EXPRESSION \ "|//" PCMK_XE_OP "//" PCMK_XE_EXPRESSION /*! * \internal * \brief Log a warning for deprecated rule syntax in operations * * \param[in] scheduler Scheduler data */ static void check_for_deprecated_rules(pcmk_scheduler_t *scheduler) { // @COMPAT Drop this function when support for the syntax is dropped xmlNode *deprecated = get_xpath_object(XPATH_DEPRECATED_RULES, scheduler->input, LOG_NEVER); if (deprecated != NULL) { pcmk__warn_once(pcmk__wo_op_attr_expr, "Support for rules with node attribute expressions in " PCMK_XE_OP " or " PCMK_XE_OP_DEFAULTS " is deprecated " "and will be dropped in a future release"); } } /* * Unpack everything * At the end you'll have: * - A list of nodes * - A list of resources (each with any dependencies on other resources) * - A list of constraints between resources and nodes * - A list of constraints between start/stop actions * - A list of nodes that need to be stonith'd * - A list of nodes that need to be shutdown * - A list of the possible stop/start actions (without dependencies) */ gboolean cluster_status(pcmk_scheduler_t * scheduler) { // @TODO Deprecate, replacing with a safer public alternative if necessary const char *new_version = NULL; xmlNode *section = NULL; if ((scheduler == NULL) || (scheduler->input == NULL)) { return FALSE; } if (pcmk_is_set(scheduler->flags, pcmk__sched_have_status)) { /* cluster_status() has already been called since the last time the * scheduler was reset. Unpacking the input CIB again would cause * duplication within the scheduler object's data structures. * * The correct return code here is not obvious. Nothing internal checks * the code, however. */ return TRUE; } new_version = crm_element_value(scheduler->input, PCMK_XA_CRM_FEATURE_SET); if (pcmk__check_feature_set(new_version) != pcmk_rc_ok) { pcmk__config_err("Can't process CIB with feature set '%s' greater than our own '%s'", new_version, CRM_FEATURE_SET); return FALSE; } crm_trace("Beginning unpack"); pcmk__xml_free(scheduler->priv->failed); scheduler->priv->failed = pcmk__xe_create(NULL, "failed-ops"); if (scheduler->priv->now == NULL) { scheduler->priv->now = crm_time_new(NULL); } if (pcmk__xe_attr_is_true(scheduler->input, PCMK_XA_HAVE_QUORUM)) { pcmk__set_scheduler_flags(scheduler, pcmk__sched_quorate); } else { pcmk__clear_scheduler_flags(scheduler, pcmk__sched_quorate); } scheduler->priv->op_defaults = get_xpath_object("//" PCMK_XE_OP_DEFAULTS, scheduler->input, LOG_NEVER); check_for_deprecated_rules(scheduler); scheduler->priv->rsc_defaults = get_xpath_object("//" PCMK_XE_RSC_DEFAULTS, scheduler->input, LOG_NEVER); section = get_xpath_object("//" PCMK_XE_CRM_CONFIG, scheduler->input, LOG_TRACE); unpack_config(section, scheduler); if (!pcmk_any_flags_set(scheduler->flags, pcmk__sched_location_only|pcmk__sched_quorate) && (scheduler->no_quorum_policy != pcmk_no_quorum_ignore)) { pcmk__sched_warn(scheduler, "Fencing and resource management disabled " "due to lack of quorum"); } section = get_xpath_object("//" PCMK_XE_NODES, scheduler->input, LOG_TRACE); unpack_nodes(section, scheduler); section = get_xpath_object("//" PCMK_XE_RESOURCES, scheduler->input, LOG_TRACE); if (!pcmk_is_set(scheduler->flags, pcmk__sched_location_only)) { unpack_remote_nodes(section, scheduler); } unpack_resources(section, scheduler); section = get_xpath_object("//" PCMK_XE_FENCING_TOPOLOGY, scheduler->input, LOG_TRACE); pcmk__validate_fencing_topology(section); section = get_xpath_object("//" PCMK_XE_TAGS, scheduler->input, LOG_NEVER); unpack_tags(section, scheduler); if (!pcmk_is_set(scheduler->flags, pcmk__sched_location_only)) { section = get_xpath_object("//" PCMK_XE_STATUS, scheduler->input, LOG_TRACE); unpack_status(section, scheduler); } if (!pcmk_is_set(scheduler->flags, pcmk__sched_no_counts)) { for (GList *item = scheduler->priv->resources; item != NULL; item = item->next) { pcmk_resource_t *rsc = item->data; rsc->priv->fns->count(item->data); } crm_trace("Cluster resource count: %d (%d disabled, %d blocked)", scheduler->priv->ninstances, scheduler->priv->disabled_resources, scheduler->priv->blocked_resources); } if ((scheduler->priv->local_node_name != NULL) && (pcmk_find_node(scheduler, scheduler->priv->local_node_name) == NULL)) { crm_info("Creating a fake local node for %s", scheduler->priv->local_node_name); pe_create_node(scheduler->priv->local_node_name, scheduler->priv->local_node_name, NULL, 0, scheduler); } pcmk__set_scheduler_flags(scheduler, pcmk__sched_have_status); return TRUE; } pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id) { return pe_find_resource_with_flags(rsc_list, id, pcmk_rsc_match_history); } pcmk_resource_t * pe_find_resource_with_flags(GList *rsc_list, const char *id, enum pe_find flags) { GList *rIter = NULL; for (rIter = rsc_list; id && rIter; rIter = rIter->next) { pcmk_resource_t *parent = rIter->data; pcmk_resource_t *match = parent->priv->fns->find_rsc(parent, id, NULL, - flags); + (uint32_t) flags); if (match != NULL) { return match; } } crm_trace("No match for %s", id); return NULL; } /*! * \brief Find a node by name or ID in a list of nodes * * \param[in] nodes List of nodes (as pcmk_node_t*) * \param[in] id If not NULL, ID of node to find * \param[in] node_name If not NULL, name of node to find * * \return Node from \p nodes that matches \p id if any, * otherwise node from \p nodes that matches \p uname if any, * otherwise NULL */ pcmk_node_t * pe_find_node_any(const GList *nodes, const char *id, const char *uname) { pcmk_node_t *match = NULL; if (id != NULL) { match = pe_find_node_id(nodes, id); } if ((match == NULL) && (uname != NULL)) { match = pcmk__find_node_in_list(nodes, uname); } return match; } /*! * \brief Find a node by ID in a list of nodes * * \param[in] nodes List of nodes (as pcmk_node_t*) * \param[in] id ID of node to find * * \return Node from \p nodes that matches \p id if any, otherwise NULL */ pcmk_node_t * pe_find_node_id(const GList *nodes, const char *id) { for (const GList *iter = nodes; iter != NULL; iter = iter->next) { pcmk_node_t *node = (pcmk_node_t *) iter->data; /* @TODO Whether node IDs should be considered case-sensitive should * probably depend on the node type, so functionizing the comparison * would be worthwhile */ if (pcmk__str_eq(node->priv->id, id, pcmk__str_casei)) { return node; } } return NULL; } // Deprecated functions kept only for backward API compatibility // LCOV_EXCL_START #include pcmk_scheduler_t * pe_new_working_set(void) { return pcmk_new_scheduler(); } void pe_reset_working_set(pcmk_scheduler_t *scheduler) { if (scheduler == NULL) { return; } pcmk_reset_scheduler(scheduler); } void cleanup_calculations(pcmk_scheduler_t *scheduler) { if (scheduler == NULL) { return; } pcmk__clear_scheduler_flags(scheduler, pcmk__sched_have_status); if (scheduler->priv->options != NULL) { g_hash_table_destroy(scheduler->priv->options); } if (scheduler->priv->singletons != NULL) { g_hash_table_destroy(scheduler->priv->singletons); } if (scheduler->priv->ticket_constraints != NULL) { g_hash_table_destroy(scheduler->priv->ticket_constraints); } if (scheduler->priv->templates != NULL) { g_hash_table_destroy(scheduler->priv->templates); } if (scheduler->priv->tags != NULL) { g_hash_table_destroy(scheduler->priv->tags); } crm_trace("deleting resources"); g_list_free_full(scheduler->priv->resources, pcmk__free_resource); crm_trace("deleting actions"); g_list_free_full(scheduler->priv->actions, pcmk__free_action); crm_trace("deleting nodes"); g_list_free_full(scheduler->nodes, pcmk__free_node); scheduler->nodes = NULL; pcmk__free_param_checks(scheduler); g_list_free(scheduler->priv->stop_needed); crm_time_free(scheduler->priv->now); pcmk__xml_free(scheduler->input); pcmk__xml_free(scheduler->priv->failed); pcmk__xml_free(scheduler->priv->graph); set_working_set_defaults(scheduler); CRM_LOG_ASSERT((scheduler->priv->location_constraints == NULL) && (scheduler->priv->ordering_constraints == NULL)); } void set_working_set_defaults(pcmk_scheduler_t *scheduler) { // These members must be preserved pcmk__scheduler_private_t *priv = scheduler->priv; pcmk__output_t *out = priv->out; char *local_node_name = scheduler->priv->local_node_name; // Wipe the main structs (any other members must have previously been freed) memset(scheduler, 0, sizeof(pcmk_scheduler_t)); memset(priv, 0, sizeof(pcmk__scheduler_private_t)); // Restore the members to preserve scheduler->priv = priv; scheduler->priv->out = out; scheduler->priv->local_node_name = local_node_name; // Set defaults for everything else pcmk__set_scheduler_defaults(scheduler); } void pe_free_working_set(pcmk_scheduler_t *scheduler) { pcmk_free_scheduler(scheduler); } pcmk_node_t * pe_find_node(const GList *nodes, const char *node_name) { return pcmk__find_node_in_list(nodes, node_name); } // LCOV_EXCL_STOP // End deprecated API