Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/crm/common/nodes.h b/include/crm/common/nodes.h
index 5f6f25ff0c..64c574ad3c 100644
--- a/include/crm/common/nodes.h
+++ b/include/crm/common/nodes.h
@@ -1,230 +1,234 @@
/*
* 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.
*/
#ifndef PCMK__CRM_COMMON_NODES__H
#define PCMK__CRM_COMMON_NODES__H
#include <stdbool.h> // bool
#include <glib.h> // gboolean, GList, GHashTable
+#include <libxml/tree.h> // xmlNode
#include <crm/common/scheduler_types.h> // pcmk_resource_t, pcmk_scheduler_t
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \file
* \brief Scheduler API for nodes
* \ingroup core
*/
// Special node attributes
#define PCMK_NODE_ATTR_MAINTENANCE "maintenance"
#define PCMK_NODE_ATTR_STANDBY "standby"
#define PCMK_NODE_ATTR_TERMINATE "terminate"
// @COMPAT Make this internal when we can break API backward compatibility
//!@{
//! \deprecated Do not use (public access will be removed in a future release)
enum node_type { // Possible node types
pcmk_node_variant_cluster = 1, // Cluster layer node
pcmk_node_variant_remote = 2, // Pacemaker Remote node
node_ping = 0, // deprecated
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
node_member = pcmk_node_variant_cluster,
node_remote = pcmk_node_variant_remote,
#endif
};
//!@}
// When to probe a resource on a node (as specified in location constraints)
// @COMPAT Make this internal when we can break API backward compatibility
//!@{
//! \deprecated Do not use (public access will be removed in a future release)
enum pe_discover_e {
pcmk_probe_always = 0, // Always probe resource on node
pcmk_probe_never = 1, // Never probe resource on node
pcmk_probe_exclusive = 2, // Probe only on designated nodes
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
pe_discover_always = pcmk_probe_always,
pe_discover_never = pcmk_probe_never,
pe_discover_exclusive = pcmk_probe_exclusive,
#endif
};
//!@}
// Basic node information (all node objects for the same node share this)
// @COMPAT Make this internal when we can break API backward compatibility
//!@{
//! \deprecated Do not use (public access will be removed in a future release)
struct pe_node_shared_s {
const char *id; // Node ID at the cluster layer
const char *uname; // Node name in cluster
enum node_type type; // Node variant
// @TODO Convert these into a flag group
// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Call pcmk_node_is_online() instead
gboolean online; // Whether online
gboolean standby; // Whether in standby mode
gboolean standby_onfail; // Whether in standby mode due to on-fail
// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Call pcmk_node_is_pending() instead
gboolean pending; // Whether controller membership is pending
// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Call !pcmk_node_is_clean() instead
gboolean unclean; // Whether node requires fencing
gboolean unseen; // Whether node has never joined cluster
// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Call pcmk_node_is_shutting_down() instead
gboolean shutdown; // Whether shutting down
gboolean expected_up; // Whether expected join state is member
gboolean is_dc; // Whether node is cluster's DC
// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Call pcmk_node_is_in_maintenance() instead
gboolean maintenance; // Whether in maintenance mode
gboolean rsc_discovery_enabled; // Whether probes are allowed on node
/*
* Whether this is a guest node whose guest resource must be recovered or a
* remote node that must be fenced
*/
gboolean remote_requires_reset;
/*
* Whether this is a Pacemaker Remote node that was fenced since it was last
* connected by the cluster
*/
gboolean remote_was_fenced;
/*
* Whether this is a Pacemaker Remote node previously marked in its
* node state as being in maintenance mode
*/
gboolean remote_maintenance;
gboolean unpacked; // Whether node history has been unpacked
/*
* Number of resources active on this node (valid after CIB status section
* has been unpacked, as long as pcmk_sched_no_counts was not set)
*/
int num_resources;
// Remote connection resource for node, if it is a Pacemaker Remote node
pcmk_resource_t *remote_rsc;
// NOTE: sbd (as of at least 1.5.2) uses this
// \deprecated Call pcmk_foreach_active_resource() instead
GList *running_rsc; // List of resources active on node
GList *allocated_rsc; // List of resources assigned to node
GHashTable *attrs; // Node attributes
GHashTable *utilization; // Node utilization attributes
GHashTable *digest_cache; // Cache of calculated resource digests
/*
* Sum of priorities of all resources active on node and on any guest nodes
* connected to this node, with +1 for promoted instances (used to compare
* nodes for PCMK_OPT_PRIORITY_FENCING_DELAY)
*/
int priority;
pcmk_scheduler_t *data_set; // Cluster that node is part of
};
//!@}
// Implementation of pcmk_node_t
// @COMPAT Make contents internal when we can break API backward compatibility
//!@{
//! \deprecated Do not use (public access will be removed in a future release)
struct pe_node_s {
int weight; // Node score for a given resource
gboolean fixed; // \deprecated Do not use
int count; // Counter reused by assignment and promotion code
// NOTE: sbd (as of at least 1.5.2) uses this
struct pe_node_shared_s *details; // Basic node information
// @COMPAT This should be enum pe_discover_e
int rsc_discover_mode; // Probe mode (enum pe_discover_e)
};
//!@}
bool pcmk_node_is_online(const pcmk_node_t *node);
bool pcmk_node_is_pending(const pcmk_node_t *node);
bool pcmk_node_is_clean(const pcmk_node_t *node);
bool pcmk_node_is_shutting_down(const pcmk_node_t *node);
bool pcmk_node_is_in_maintenance(const pcmk_node_t *node);
bool pcmk_foreach_active_resource(pcmk_node_t *node,
bool (*fn)(pcmk_resource_t *, void *),
void *user_data);
+
+const char *pcmk_cib_node_shutdown(xmlNode *cib, const char *node);
+
/*!
* \internal
* \brief Return a string suitable for logging as a node name
*
* \param[in] node Node to return a node name string for
*
* \return Node name if available, otherwise node ID if available,
* otherwise "unspecified node" if node is NULL or "unidentified node"
* if node has neither a name nor ID.
*/
static inline const char *
pcmk__node_name(const pcmk_node_t *node)
{
if (node == NULL) {
return "unspecified node";
} else if (node->details->uname != NULL) {
return node->details->uname;
} else if (node->details->id != NULL) {
return node->details->id;
} else {
return "unidentified node";
}
}
/*!
* \internal
* \brief Check whether two node objects refer to the same node
*
* \param[in] node1 First node object to compare
* \param[in] node2 Second node object to compare
*
* \return true if \p node1 and \p node2 refer to the same node
*/
static inline bool
pcmk__same_node(const pcmk_node_t *node1, const pcmk_node_t *node2)
{
return (node1 != NULL) && (node2 != NULL)
&& (node1->details == node2->details);
}
#ifdef __cplusplus
}
#endif
#endif // PCMK__CRM_COMMON_NODES__H
diff --git a/lib/common/nodes.c b/lib/common/nodes.c
index 4edeafbc42..5228ea2618 100644
--- a/lib/common/nodes.c
+++ b/lib/common/nodes.c
@@ -1,163 +1,193 @@
/*
* Copyright 2022-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 <crm_internal.h>
#include <libxml/tree.h> // xmlNode
#include <crm/common/nvpair.h>
/*!
* \internal
* \brief Check whether a node is online
*
* \param[in] node Node to check
*
* \return true if \p node is online, otherwise false
*/
bool
pcmk_node_is_online(const pcmk_node_t *node)
{
return (node != NULL) && node->details->online;
}
/*!
* \internal
* \brief Check whether a node is pending
*
* Check whether a node is pending. A node is pending if it is a member of the
* cluster but not the controller group, which means it is in the process of
* either joining or leaving the cluster.
*
* \param[in] node Node to check
*
* \return true if \p node is pending, otherwise false
*/
bool
pcmk_node_is_pending(const pcmk_node_t *node)
{
return (node != NULL) && node->details->pending;
}
/*!
* \internal
* \brief Check whether a node is clean
*
* Check whether a node is clean. A node is clean if it is a cluster node or
* remote node that has been seen by the cluster at least once, or the
* startup-fencing cluster option is false; and the node, and its host if a
* guest or bundle node, are not scheduled to be fenced.
*
* \param[in] node Node to check
*
* \return true if \p node is clean, otherwise false
*/
bool
pcmk_node_is_clean(const pcmk_node_t *node)
{
return (node != NULL) && !(node->details->unclean);
}
/*!
* \internal
* \brief Check whether a node is shutting down
*
* \param[in] node Node to check
*
* \return true if \p node is shutting down, otherwise false
*/
bool
pcmk_node_is_shutting_down(const pcmk_node_t *node)
{
return (node != NULL) && node->details->shutdown;
}
/*!
* \internal
* \brief Check whether a node is in maintenance mode
*
* \param[in] node Node to check
*
* \return true if \p node is in maintenance mode, otherwise false
*/
bool
pcmk_node_is_in_maintenance(const pcmk_node_t *node)
{
return (node != NULL) && node->details->maintenance;
}
/*!
* \internal
* \brief Call a function for each resource active on a node
*
* Call a caller-supplied function with a caller-supplied argument for each
* resource that is active on a given node. If the function returns false, this
* function will return immediately without processing any remaining resources.
*
* \param[in] node Node to check
*
* \return Result of last call of \p fn (or false if none)
*/
bool
pcmk_foreach_active_resource(pcmk_node_t *node,
bool (*fn)(pcmk_resource_t *, void *),
void *user_data)
{
bool result = false;
if ((node != NULL) && (fn != NULL)) {
for (GList *item = node->details->running_rsc; item != NULL;
item = item->next) {
result = fn((pcmk_resource_t *) item->data, user_data);
if (!result) {
break;
}
}
}
return result;
}
void
pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid)
{
CRM_ASSERT(xml != NULL);
if (node != NULL) {
crm_xml_add(xml, PCMK__XA_ATTR_HOST, node);
}
if (nodeid > 0) {
crm_xml_add_int(xml, PCMK__XA_ATTR_HOST_ID, nodeid);
}
}
/*!
* \internal
* \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 *
pcmk__find_node_in_list(const GList *nodes, const char *node_name)
{
if (node_name != NULL) {
for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
pcmk_node_t *node = (pcmk_node_t *) iter->data;
if (pcmk__str_eq(node->details->uname, node_name,
pcmk__str_casei)) {
return node;
}
}
}
return NULL;
}
+
+#define XP_SHUTDOWN "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_UNAME "='%s']/" \
+ PCMK__XE_TRANSIENT_ATTRIBUTES "/" PCMK_XE_INSTANCE_ATTRIBUTES "/" \
+ PCMK_XE_NVPAIR "[@" PCMK_XA_NAME "='" PCMK__NODE_ATTR_SHUTDOWN "']"
+
+/*!
+ * \brief Get value of a node's shutdown attribute from CIB, if present
+ *
+ * \param[in] cib CIB to check
+ * \param[in] node Name of node to check
+ *
+ * \return Value of shutdown attribute for \p node in \p cib if any,
+ * otherwise NULL
+ * \note The return value is a pointer into \p cib and so is valid only for the
+ * lifetime of that object.
+ */
+const char *
+pcmk_cib_node_shutdown(xmlNode *cib, const char *node)
+{
+ if ((cib != NULL) && (node != NULL)) {
+ char *xpath = crm_strdup_printf(XP_SHUTDOWN, node);
+ xmlNode *match = get_xpath_object(xpath, cib, LOG_TRACE);
+
+ free(xpath);
+ if (match != NULL) {
+ return crm_element_value(match, PCMK_XA_VALUE);
+ }
+ }
+ return NULL;
+}
diff --git a/lib/common/tests/nodes/Makefile.am b/lib/common/tests/nodes/Makefile.am
index f52c615e4d..6c4964e1d0 100644
--- a/lib/common/tests/nodes/Makefile.am
+++ b/lib/common/tests/nodes/Makefile.am
@@ -1,23 +1,24 @@
#
# 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 General Public License version 2
# or later (GPLv2+) WITHOUT ANY WARRANTY.
#
include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
check_PROGRAMS = pcmk__find_node_in_list_test \
+ pcmk__xe_add_node_test \
+ pcmk_cib_node_shutdown_test \
pcmk_foreach_active_resource_test \
pcmk_node_is_clean_test \
pcmk_node_is_in_maintenance_test \
pcmk_node_is_online_test \
pcmk_node_is_pending_test \
- pcmk_node_is_shutting_down_test \
- pcmk__xe_add_node_test
+ pcmk_node_is_shutting_down_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/nodes/pcmk_cib_node_shutdown_test.c b/lib/common/tests/nodes/pcmk_cib_node_shutdown_test.c
new file mode 100644
index 0000000000..a64a7283e8
--- /dev/null
+++ b/lib/common/tests/nodes/pcmk_cib_node_shutdown_test.c
@@ -0,0 +1,70 @@
+/*
+ * 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 General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <stdio.h> // NULL
+#include <libxml/tree.h> // xmlNode
+
+#include <crm/common/nodes.h>
+#include <crm/common/unittest_internal.h>
+
+// Minimum CIB structure needed for function's XPath search
+#define CIB_XML \
+ "<" PCMK_XE_CIB ">" \
+ "<" PCMK_XE_STATUS ">" \
+ "<" PCMK__XE_NODE_STATE " " PCMK_XA_UNAME "='node1'>" \
+ "<" PCMK__XE_TRANSIENT_ATTRIBUTES ">" \
+ "<" PCMK_XE_INSTANCE_ATTRIBUTES ">" \
+ "<" PCMK_XE_NVPAIR " " \
+ PCMK_XA_NAME "='" PCMK__NODE_ATTR_SHUTDOWN "' " \
+ PCMK_XA_VALUE "='999'/>" \
+ "</" PCMK_XE_INSTANCE_ATTRIBUTES ">" \
+ "</" PCMK__XE_TRANSIENT_ATTRIBUTES ">" \
+ "</" PCMK__XE_NODE_STATE ">" \
+ "</" PCMK_XE_STATUS ">" \
+ "</" PCMK_XE_CIB ">"
+
+static void
+null_args(void **state)
+{
+ xmlNode *xml = pcmk__xml_parse(CIB_XML);
+
+ assert_non_null(xml);
+ assert_null(pcmk_cib_node_shutdown(NULL, NULL));
+ assert_null(pcmk_cib_node_shutdown(xml, NULL));
+ assert_null(pcmk_cib_node_shutdown(NULL, "node1"));
+ free_xml(xml);
+}
+
+static void
+shutdown_absent(void **state)
+{
+ xmlNode *xml = pcmk__xml_parse(CIB_XML);
+
+ assert_non_null(xml);
+ assert_null(pcmk_cib_node_shutdown(xml, "node"));
+ assert_null(pcmk_cib_node_shutdown(xml, "node10"));
+ free_xml(xml);
+}
+
+static void
+shutdown_present(void **state)
+{
+ xmlNode *xml = pcmk__xml_parse(CIB_XML);
+
+ assert_non_null(xml);
+ assert_string_equal(pcmk_cib_node_shutdown(xml, "node1"), "999");
+ free_xml(xml);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(null_args),
+ cmocka_unit_test(shutdown_absent),
+ cmocka_unit_test(shutdown_present))

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 25, 3:59 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1952073
Default Alt Text
(17 KB)

Event Timeline