diff --git a/include/crm/common/action_relation_internal.h b/include/crm/common/action_relation_internal.h index 2f594b3edf..895bf29eca 100644 --- a/include/crm/common/action_relation_internal.h +++ b/include/crm/common/action_relation_internal.h @@ -1,188 +1,191 @@ /* * Copyright 2023-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PCMK__CRM_COMMON_ACTION_RELATION_INTERNAL__H #define PCMK__CRM_COMMON_ACTION_RELATION_INTERNAL__H #include // bool #include // uint32_t +#include // gpointer #include // pcmk_resource_t, pcmk_action_t #ifdef __cplusplus extern "C" { #endif // Flags to indicate the relationship between two actions enum pcmk__action_relation_flags { //! No relation (compare with equality rather than bit set) pcmk__ar_none = 0U, //! Actions are ordered (optionally, if no other flags are set) pcmk__ar_ordered = (1U << 0), //! Relation applies only if 'first' cannot be part of a live migration pcmk__ar_if_first_unmigratable = (1U << 1), /*! * If 'then' is required, 'first' becomes required (and becomes unmigratable * if 'then' is); also, if 'first' is a stop of a blocked resource, 'then' * becomes unrunnable */ pcmk__ar_then_implies_first = (1U << 4), /*! * If 'first' is required, 'then' becomes required; if 'first' is a stop of * a blocked resource, 'then' becomes unrunnable */ pcmk__ar_first_implies_then = (1U << 5), /*! * If 'then' is required and for a promoted instance, 'first' becomes * required (and becomes unmigratable if 'then' is) */ pcmk__ar_promoted_then_implies_first = (1U << 6), /*! * 'first' is runnable only if 'then' is both runnable and migratable, * and 'first' becomes required if 'then' is */ pcmk__ar_unmigratable_then_blocks = (1U << 7), //! 'then' is runnable (and migratable) only if 'first' is runnable pcmk__ar_unrunnable_first_blocks = (1U << 8), //! If 'first' is unrunnable, 'then' becomes a real, unmigratable action pcmk__ar_first_else_then = (1U << 9), //! If 'first' is required, 'then' action for instance on same node is pcmk__ar_first_implies_same_node_then = (1U << 10), /*! * Disable relation if 'first' is unrunnable and for an active resource, * otherwise order actions and make 'then' unrunnable if 'first' is. * * This is used to order a bundle replica's start of its container before a * probe of its remote connection resource, in case the connection uses the * REMOTE_CONTAINER_HACK to replace the connection address with where the * container is running. */ pcmk__ar_nested_remote_probe = (1U << 11), /*! * If 'first' is for a blocked resource, make 'then' unrunnable. * * If 'then' is required, make 'first' required, make 'first' unmigratable * if 'then' is unmigratable, and make 'then' unrunnable if 'first' is * unrunnable. * * If 'then' is unrunnable and for the same resource as 'first', make * 'first' required if it is runnable, and make 'first' unmigratable if * 'then' is unmigratable. * * This is used for "stop then start primitive" (restarts) and * "stop group member then stop previous member". */ pcmk__ar_intermediate_stop = (1U << 12), /*! * The actions must be serialized if in the same transition but can be in * either order. (In practice, we always arrange them as 'first' then * 'then', so they end up being essentially the same as optional orderings.) * * @TODO Handle more intelligently -- for example, we could schedule the * action with the fewest inputs first, so we're more likely to execute at * least one if there is a failure during the transition. Or, we could * prefer certain action types over others, or base it on resource priority. */ pcmk__ar_serialize = (1U << 14), //! Relation applies only if actions are on same node pcmk__ar_if_on_same_node = (1U << 15), //! If 'then' is required, 'first' must be added to the transition graph pcmk__ar_then_implies_first_graphed = (1U << 16), //! If 'first' is required and runnable, 'then' must be in graph pcmk__ar_first_implies_then_graphed = (1U << 17), //! User-configured asymmetric ordering pcmk__ar_asymmetric = (1U << 20), //! Actions are ordered if on same node (or migration target for migrate_to) pcmk__ar_if_on_same_node_or_target = (1U << 21), //! 'then' action is runnable if certain number of 'first' instances are pcmk__ar_min_runnable = (1U << 22), //! Ordering applies only if 'first' is required and on same node as 'then' pcmk__ar_if_required_on_same_node = (1U << 23), //! Ordering applies even if 'first' runs on guest node created by 'then' pcmk__ar_guest_allowed = (1U << 24), //! If 'then' action becomes required, 'first' becomes optional pcmk__ar_then_cancels_first = (1U << 25), }; /* Action relation object * * The most common type of relation is an ordering, in which case action1 etc. * refers to the "first" action, and action2 etc. refers to the "then" action. */ typedef struct { int id; // Counter to identify relation uint32_t flags; // Group of enum pcmk__action_relation_flags pcmk_resource_t *rsc1; // Resource for first action, if any pcmk_action_t *action1; // First action in relation char *task1; // Action name or key for first action pcmk_resource_t *rsc2; // Resource for second action, if any pcmk_action_t *action2; // Second action in relation char *task2; // Action name or key for second action } pcmk__action_relation_t; // Action sequenced relative to another action typedef struct pcmk__related_action { pcmk_action_t *action; // Action to be sequenced uint32_t flags; // Group of enum pcmk__action_relation_flags bool graphed; // Whether action has been added to graph yet } pcmk__related_action_t; /*! * \internal * \brief Set action relation flags * * \param[in,out] ar_flags Flag group to modify * \param[in] flags_to_set enum pcmk__action_relation_flags to set */ #define pcmk__set_relation_flags(ar_flags, flags_to_set) do { \ ar_flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \ "Action relation", "constraint", \ ar_flags, (flags_to_set), \ #flags_to_set); \ } while (0) /*! * \internal * \brief Clear action relation flags * * \param[in,out] ar_flags Flag group to modify * \param[in] flags_to_clear enum pcmk__action_relation_flags to clear */ #define pcmk__clear_relation_flags(ar_flags, flags_to_clear) do { \ ar_flags = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, \ "Action relation", "constraint", \ ar_flags, (flags_to_clear), \ #flags_to_clear); \ } while (0) +void pcmk__free_action_relation(gpointer user_data); + #ifdef __cplusplus } #endif #endif // PCMK__CRM_COMMON_ACTION_RELATION_INTERNAL__H diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 45fd0a0265..cc8d70a1e1 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -1,142 +1,143 @@ # # Copyright 2004-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # include $(top_srcdir)/mk/common.mk ## libraries lib_LTLIBRARIES = libcrmcommon.la check_LTLIBRARIES = libcrmcommon_test.la # Disable -Wcast-qual if used, because we do some hacky casting, # and because libxml2 has some signatures that should be const but aren't # for backward compatibility reasons. # s390 needs -fPIC # s390-suse-linux/bin/ld: .libs/ipc.o: relocation R_390_PC32DBL against `__stack_chk_fail@@GLIBC_2.4' can not be used when making a shared object; recompile with -fPIC CFLAGS = $(CFLAGS_COPY:-Wcast-qual=) -fPIC # Without "." here, check-recursive will run through the subdirectories first # and then run "make check" here. This will fail, because there's things in # the subdirectories that need check_LTLIBRARIES built first. Adding "." here # changes the order so the subdirectories are processed afterwards. SUBDIRS = . tests noinst_HEADERS = crmcommon_private.h \ mock_private.h libcrmcommon_la_LDFLAGS = -version-info 68:0:0 libcrmcommon_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libcrmcommon_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) # If configured with --with-profiling or --with-coverage, BUILD_PROFILING will # be set and -fno-builtin will be added to the CFLAGS. However, libcrmcommon # uses the fabs() function which is normally supplied by gcc as one of its # builtins. Therefore we need to explicitly link against libm here or the # tests won't link. if BUILD_PROFILING libcrmcommon_la_LIBADD = -lm endif ## Library sources (*must* use += format for bumplibs) libcrmcommon_la_SOURCES = libcrmcommon_la_SOURCES += acl.c +libcrmcommon_la_SOURCES += action_relation.c libcrmcommon_la_SOURCES += actions.c libcrmcommon_la_SOURCES += agents.c libcrmcommon_la_SOURCES += alerts.c libcrmcommon_la_SOURCES += attrs.c libcrmcommon_la_SOURCES += cib.c if BUILD_CIBSECRETS libcrmcommon_la_SOURCES += cib_secrets.c endif libcrmcommon_la_SOURCES += cmdline.c libcrmcommon_la_SOURCES += digest.c libcrmcommon_la_SOURCES += health.c libcrmcommon_la_SOURCES += io.c libcrmcommon_la_SOURCES += ipc_attrd.c libcrmcommon_la_SOURCES += ipc_client.c libcrmcommon_la_SOURCES += ipc_common.c libcrmcommon_la_SOURCES += ipc_controld.c libcrmcommon_la_SOURCES += ipc_pacemakerd.c libcrmcommon_la_SOURCES += ipc_schedulerd.c libcrmcommon_la_SOURCES += ipc_server.c libcrmcommon_la_SOURCES += iso8601.c libcrmcommon_la_SOURCES += lists.c libcrmcommon_la_SOURCES += logging.c libcrmcommon_la_SOURCES += mainloop.c libcrmcommon_la_SOURCES += messages.c libcrmcommon_la_SOURCES += nodes.c libcrmcommon_la_SOURCES += nvpair.c libcrmcommon_la_SOURCES += options.c libcrmcommon_la_SOURCES += options_display.c libcrmcommon_la_SOURCES += output.c libcrmcommon_la_SOURCES += output_html.c libcrmcommon_la_SOURCES += output_log.c libcrmcommon_la_SOURCES += output_none.c libcrmcommon_la_SOURCES += output_text.c libcrmcommon_la_SOURCES += output_xml.c libcrmcommon_la_SOURCES += patchset.c libcrmcommon_la_SOURCES += patchset_display.c libcrmcommon_la_SOURCES += pid.c libcrmcommon_la_SOURCES += probes.c libcrmcommon_la_SOURCES += procfs.c libcrmcommon_la_SOURCES += remote.c libcrmcommon_la_SOURCES += resources.c libcrmcommon_la_SOURCES += results.c libcrmcommon_la_SOURCES += roles.c libcrmcommon_la_SOURCES += rules.c libcrmcommon_la_SOURCES += scheduler.c libcrmcommon_la_SOURCES += schemas.c libcrmcommon_la_SOURCES += scores.c libcrmcommon_la_SOURCES += servers.c libcrmcommon_la_SOURCES += strings.c libcrmcommon_la_SOURCES += tls.c libcrmcommon_la_SOURCES += utils.c libcrmcommon_la_SOURCES += watchdog.c libcrmcommon_la_SOURCES += xml.c libcrmcommon_la_SOURCES += xml_attr.c libcrmcommon_la_SOURCES += xml_comment.c libcrmcommon_la_SOURCES += xml_display.c libcrmcommon_la_SOURCES += xml_element.c libcrmcommon_la_SOURCES += xml_idref.c libcrmcommon_la_SOURCES += xml_io.c libcrmcommon_la_SOURCES += xpath.c # # libcrmcommon_test is used only with unit tests, so we can mock system calls. # See mock.c for details. # include $(top_srcdir)/mk/tap.mk libcrmcommon_test_la_SOURCES = $(libcrmcommon_la_SOURCES) libcrmcommon_test_la_SOURCES += mock.c libcrmcommon_test_la_SOURCES += unittest.c libcrmcommon_test_la_LDFLAGS = $(libcrmcommon_la_LDFLAGS) \ -rpath $(libdir) \ $(LDFLAGS_WRAP) # If GCC emits a builtin function in place of something we've mocked up, that will # get used instead of the mocked version which leads to unexpected test results. So # disable all builtins. Older versions of GCC (at least, on RHEL7) will still emit # replacement code for strdup (and possibly other functions) unless -fno-inline is # also added. libcrmcommon_test_la_CFLAGS = $(libcrmcommon_la_CFLAGS) \ -DPCMK__UNIT_TESTING \ -fno-builtin \ -fno-inline # If -fno-builtin is used, -lm also needs to be added. See the comment at # BUILD_PROFILING above. libcrmcommon_test_la_LIBADD = $(libcrmcommon_la_LIBADD) if BUILD_COVERAGE libcrmcommon_test_la_LIBADD += -lgcov endif libcrmcommon_test_la_LIBADD += -lcmocka libcrmcommon_test_la_LIBADD += -lm nodist_libcrmcommon_test_la_SOURCES = $(nodist_libcrmcommon_la_SOURCES) diff --git a/lib/common/action_relation.c b/lib/common/action_relation.c new file mode 100644 index 0000000000..22343cc551 --- /dev/null +++ b/lib/common/action_relation.c @@ -0,0 +1,31 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include + +#include // free() +#include // gpointer + +#include + +/*! + * \internal + * \brief Free an action relation + * + * \param[in,out] user_data Action relation to free + */ +void +pcmk__free_action_relation(gpointer user_data) +{ + pcmk__action_relation_t *relation = user_data; + + free(relation->task1); + free(relation->task2); + free(relation); +} diff --git a/lib/pengine/status.c b/lib/pengine/status.c index 4b37cb7db7..2cea3464b6 100644 --- a/lib/pengine/status.c +++ b/lib/pengine/status.c @@ -1,464 +1,446 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include /*! * \brief Create a new object to hold scheduler data * * \return New, initialized scheduler data on success, else NULL (and set errno) * \note Only pcmk_scheduler_t objects created with this function (as opposed * to statically declared or directly allocated) should be used with the * functions in this library, to allow for future extensions to the * data type. The caller is responsible for freeing the memory with * pe_free_working_set() when the instance is no longer needed. */ pcmk_scheduler_t * pe_new_working_set(void) { pcmk_scheduler_t *scheduler = calloc(1, sizeof(pcmk_scheduler_t)); if (scheduler == NULL) { return NULL; } scheduler->priv = calloc(1, sizeof(pcmk__scheduler_private_t)); if (scheduler->priv == NULL) { free(scheduler); return NULL; } pcmk__set_scheduler_defaults(scheduler); return scheduler; } /*! * \brief Free scheduler data * * \param[in,out] scheduler Scheduler data to free */ void pe_free_working_set(pcmk_scheduler_t *scheduler) { if (scheduler != NULL) { pe_reset_working_set(scheduler); free(scheduler->priv->local_node_name); free(scheduler->priv); free(scheduler); } } #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) { const char *new_version = NULL; xmlNode *section = NULL; if ((scheduler == NULL) || (scheduler->input == NULL)) { return FALSE; } 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"); if (scheduler->priv->failed != NULL) { 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; } static void pe_free_actions(GList *actions) { GList *iterator = actions; while (iterator != NULL) { pe_free_action(iterator->data); iterator = iterator->next; } if (actions != NULL) { g_list_free(actions); } } -static void -pe__free_ordering(GList *constraints) -{ - GList *iterator = constraints; - - while (iterator != NULL) { - pcmk__action_relation_t *order = iterator->data; - - iterator = iterator->next; - - free(order->task1); - free(order->task2); - free(order); - } - if (constraints != NULL) { - g_list_free(constraints); - } -} - static void pe__free_location(GList *constraints) { GList *iterator = constraints; while (iterator != NULL) { pcmk__location_t *cons = iterator->data; iterator = iterator->next; g_list_free_full(cons->nodes, pcmk__free_node_copy); free(cons->id); free(cons); } if (constraints != NULL) { g_list_free(constraints); } } /*! * \brief Reset scheduler data to defaults without freeing it or constraints * * \param[in,out] scheduler Scheduler data to reset * * \deprecated This function is deprecated as part of the API; * pe_reset_working_set() should be used instead. */ 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"); pe_free_actions(scheduler->priv->actions); 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)); } /*! * \brief Reset scheduler data to default state without freeing it * * \param[in,out] scheduler Scheduler data to reset */ void pe_reset_working_set(pcmk_scheduler_t *scheduler) { if (scheduler == NULL) { return; } crm_trace("Deleting %d ordering constraints", g_list_length(scheduler->priv->ordering_constraints)); - pe__free_ordering(scheduler->priv->ordering_constraints); + g_list_free_full(scheduler->priv->ordering_constraints, + pcmk__free_action_relation); scheduler->priv->ordering_constraints = NULL; crm_trace("Deleting %d location constraints", g_list_length(scheduler->priv->location_constraints)); pe__free_location(scheduler->priv->location_constraints); scheduler->priv->location_constraints = NULL; crm_trace("Deleting %d colocation constraints", g_list_length(scheduler->priv->colocation_constraints)); g_list_free_full(scheduler->priv->colocation_constraints, free); scheduler->priv->colocation_constraints = NULL; cleanup_calculations(scheduler); } 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); } 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); 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 /*! * \brief Find a node by name in a list of nodes * * \param[in] nodes List of nodes (as pcmk_node_t*) * \param[in] node_name Name of node to find * * \return Node from \p nodes that matches \p node_name if any, otherwise NULL */ 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