/src/pacemaker/lib/common/nodes.c
Line | Count | Source |
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) |
23 | 0 | { |
24 | 0 | pcmk_node_t *node = user_data; |
25 | |
|
26 | 0 | if (node == NULL) { |
27 | 0 | return; |
28 | 0 | } |
29 | 0 | if (node->details == NULL) { |
30 | 0 | free(node); |
31 | 0 | return; |
32 | 0 | } |
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 | 0 | crm_trace("Freeing node %s", (pcmk__is_pacemaker_remote_node(node)? |
38 | 0 | "(guest or remote)" : pcmk__node_name(node))); |
39 | | |
40 | 0 | if (node->priv->attrs != NULL) { |
41 | 0 | g_hash_table_destroy(node->priv->attrs); |
42 | 0 | } |
43 | 0 | if (node->priv->utilization != NULL) { |
44 | 0 | g_hash_table_destroy(node->priv->utilization); |
45 | 0 | } |
46 | 0 | if (node->priv->digest_cache != NULL) { |
47 | 0 | g_hash_table_destroy(node->priv->digest_cache); |
48 | 0 | } |
49 | 0 | g_list_free(node->details->running_rsc); |
50 | 0 | g_list_free(node->priv->assigned_resources); |
51 | 0 | free(node->priv); |
52 | 0 | free(node->details); |
53 | 0 | free(node->assign); |
54 | 0 | free(node); |
55 | 0 | } |
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) |
65 | 0 | { |
66 | 0 | if (data != NULL) { |
67 | 0 | pcmk_node_t *node = data; |
68 | |
|
69 | 0 | if (node->assign != NULL) { |
70 | | // This is the only member allocated separately for a node copy |
71 | 0 | free(node->assign); |
72 | 0 | } |
73 | 0 | free(node); |
74 | 0 | } |
75 | 0 | } |
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) |
87 | 0 | { |
88 | 0 | return (node != NULL) && node->details->online; |
89 | 0 | } |
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) |
105 | 0 | { |
106 | 0 | return (node != NULL) && node->details->pending; |
107 | 0 | } |
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) |
124 | 0 | { |
125 | 0 | return (node != NULL) && !(node->details->unclean); |
126 | 0 | } |
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) |
138 | 0 | { |
139 | 0 | return (node != NULL) && node->details->shutdown; |
140 | 0 | } |
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) |
152 | 0 | { |
153 | 0 | return (node != NULL) && node->details->maintenance; |
154 | 0 | } |
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, |
170 | | bool (*fn)(pcmk_resource_t *, void *), |
171 | | void *user_data) |
172 | 0 | { |
173 | 0 | bool result = false; |
174 | |
|
175 | 0 | if ((node != NULL) && (fn != NULL)) { |
176 | 0 | for (GList *item = node->details->running_rsc; item != NULL; |
177 | 0 | item = item->next) { |
178 | |
|
179 | 0 | result = fn((pcmk_resource_t *) item->data, user_data); |
180 | 0 | if (!result) { |
181 | 0 | break; |
182 | 0 | } |
183 | 0 | } |
184 | 0 | } |
185 | 0 | return result; |
186 | 0 | } |
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) |
199 | 0 | { |
200 | 0 | if (node_name != NULL) { |
201 | 0 | for (const GList *iter = nodes; iter != NULL; iter = iter->next) { |
202 | 0 | pcmk_node_t *node = (pcmk_node_t *) iter->data; |
203 | |
|
204 | 0 | if (pcmk__str_eq(node->priv->name, node_name, pcmk__str_casei)) { |
205 | 0 | return node; |
206 | 0 | } |
207 | 0 | } |
208 | 0 | } |
209 | 0 | return NULL; |
210 | 0 | } |
211 | | |
212 | 0 | #define XP_SHUTDOWN "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_UNAME "='%s']/" \ |
213 | 0 | PCMK__XE_TRANSIENT_ATTRIBUTES "/" PCMK_XE_INSTANCE_ATTRIBUTES "/" \ |
214 | 0 | 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) |
229 | 0 | { |
230 | 0 | if ((cib != NULL) && (node != NULL)) { |
231 | 0 | char *xpath = pcmk__assert_asprintf(XP_SHUTDOWN, node); |
232 | 0 | xmlNode *match = pcmk__xpath_find_one(cib->doc, xpath, LOG_TRACE); |
233 | |
|
234 | 0 | free(xpath); |
235 | 0 | if (match != NULL) { |
236 | 0 | return pcmk__xe_get(match, PCMK_XA_VALUE); |
237 | 0 | } |
238 | 0 | } |
239 | 0 | return NULL; |
240 | 0 | } |