1 /*
2 * Copyright 2022-2025 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <libxml/tree.h> // xmlNode
13 #include <crm/common/nvpair.h>
14
15 /*!
16 * \internal
17 * \brief Free a node object
18 *
19 * \param[in,out] user_data Node object to free
20 */
21 void
22 pcmk__free_node(gpointer user_data)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
23 {
24 pcmk_node_t *node = user_data;
25
26 if (node == NULL) {
27 return;
28 }
29 if (node->details == NULL) {
30 free(node);
31 return;
32 }
33
34 /* This may be called after freeing resources, which means that we can't
35 * use node->private->name for Pacemaker Remote nodes.
36 */
37 crm_trace("Freeing node %s", (pcmk__is_pacemaker_remote_node(node)?
38 "(guest or remote)" : pcmk__node_name(node)));
39
40 if (node->priv->attrs != NULL) {
41 g_hash_table_destroy(node->priv->attrs);
42 }
43 if (node->priv->utilization != NULL) {
44 g_hash_table_destroy(node->priv->utilization);
45 }
46 if (node->priv->digest_cache != NULL) {
47 g_hash_table_destroy(node->priv->digest_cache);
48 }
49 g_list_free(node->details->running_rsc);
50 g_list_free(node->priv->assigned_resources);
51 free(node->priv);
52 free(node->details);
53 free(node->assign);
54 free(node);
55 }
56
57 /*!
58 * \internal
59 * \brief Free a copy of a node object
60 *
61 * \param[in] data Node copy (created by pe__copy_node()) to free
62 */
63 void
64 pcmk__free_node_copy(void *data)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
65 {
66 if (data != NULL) {
67 pcmk_node_t *node = data;
68
69 if (node->assign != NULL) {
70 // This is the only member allocated separately for a node copy
71 free(node->assign);
72 }
73 free(node);
74 }
75 }
76
77 /*!
78 * \internal
79 * \brief Check whether a node is online
80 *
81 * \param[in] node Node to check
82 *
83 * \return true if \p node is online, otherwise false
84 */
85 bool
86 pcmk_node_is_online(const pcmk_node_t *node)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
87 {
88 return (node != NULL) && node->details->online;
89 }
90
91 /*!
92 * \internal
93 * \brief Check whether a node is pending
94 *
95 * Check whether a node is pending. A node is pending if it is a member of the
96 * cluster but not the controller group, which means it is in the process of
97 * either joining or leaving the cluster.
98 *
99 * \param[in] node Node to check
100 *
101 * \return true if \p node is pending, otherwise false
102 */
103 bool
104 pcmk_node_is_pending(const pcmk_node_t *node)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
105 {
106 return (node != NULL) && node->details->pending;
107 }
108
109 /*!
110 * \internal
111 * \brief Check whether a node is clean
112 *
113 * Check whether a node is clean. A node is clean if it is a cluster node or
114 * remote node that has been seen by the cluster at least once, or the
115 * startup-fencing cluster option is false; and the node, and its host if a
116 * guest or bundle node, are not scheduled to be fenced.
117 *
118 * \param[in] node Node to check
119 *
120 * \return true if \p node is clean, otherwise false
121 */
122 bool
123 pcmk_node_is_clean(const pcmk_node_t *node)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
124 {
125 return (node != NULL) && !(node->details->unclean);
126 }
127
128 /*!
129 * \internal
130 * \brief Check whether a node is shutting down
131 *
132 * \param[in] node Node to check
133 *
134 * \return true if \p node is shutting down, otherwise false
135 */
136 bool
137 pcmk_node_is_shutting_down(const pcmk_node_t *node)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
138 {
139 return (node != NULL) && node->details->shutdown;
140 }
141
142 /*!
143 * \internal
144 * \brief Check whether a node is in maintenance mode
145 *
146 * \param[in] node Node to check
147 *
148 * \return true if \p node is in maintenance mode, otherwise false
149 */
150 bool
151 pcmk_node_is_in_maintenance(const pcmk_node_t *node)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
152 {
153 return (node != NULL) && node->details->maintenance;
154 }
155
156 /*!
157 * \internal
158 * \brief Call a function for each resource active on a node
159 *
160 * Call a caller-supplied function with a caller-supplied argument for each
161 * resource that is active on a given node. If the function returns false, this
162 * function will return immediately without processing any remaining resources.
163 *
164 * \param[in] node Node to check
165 *
166 * \return Result of last call of \p fn (or false if none)
167 */
168 bool
169 pcmk_foreach_active_resource(pcmk_node_t *node,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
170 bool (*fn)(pcmk_resource_t *, void *),
171 void *user_data)
172 {
173 bool result = false;
174
175 if ((node != NULL) && (fn != NULL)) {
176 for (GList *item = node->details->running_rsc; item != NULL;
177 item = item->next) {
178
179 result = fn((pcmk_resource_t *) item->data, user_data);
180 if (!result) {
181 break;
182 }
183 }
184 }
185 return result;
186 }
187
188 /*!
189 * \internal
190 * \brief Find a node by name in a list of nodes
191 *
192 * \param[in] nodes List of nodes (as pcmk_node_t*)
193 * \param[in] node_name Name of node to find
194 *
195 * \return Node from \p nodes that matches \p node_name if any, otherwise NULL
196 */
197 pcmk_node_t *
198 pcmk__find_node_in_list(const GList *nodes, const char *node_name)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
199 {
200 if (node_name != NULL) {
201 for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
202 pcmk_node_t *node = (pcmk_node_t *) iter->data;
203
204 if (pcmk__str_eq(node->priv->name, node_name, pcmk__str_casei)) {
205 return node;
206 }
207 }
208 }
209 return NULL;
210 }
211
212 #define XP_SHUTDOWN "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_UNAME "='%s']/" \
213 PCMK__XE_TRANSIENT_ATTRIBUTES "/" PCMK_XE_INSTANCE_ATTRIBUTES "/" \
214 PCMK_XE_NVPAIR "[@" PCMK_XA_NAME "='" PCMK__NODE_ATTR_SHUTDOWN "']"
215
216 /*!
217 * \brief Get value of a node's shutdown attribute from CIB, if present
218 *
219 * \param[in] cib CIB to check
220 * \param[in] node Name of node to check
221 *
222 * \return Value of shutdown attribute for \p node in \p cib if any,
223 * otherwise NULL
224 * \note The return value is a pointer into \p cib and so is valid only for the
225 * lifetime of that object.
226 */
227 const char *
228 pcmk_cib_node_shutdown(xmlNode *cib, const char *node)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
229 {
230 if ((cib != NULL) && (node != NULL)) {
231 char *xpath = crm_strdup_printf(XP_SHUTDOWN, node);
232 xmlNode *match = pcmk__xpath_find_one(cib->doc, xpath, LOG_TRACE);
233
234 free(xpath);
235 if (match != NULL) {
236 return crm_element_value(match, PCMK_XA_VALUE);
237 }
238 }
239 return NULL;
240 }