1 /*
2 * Copyright 2022-2024 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 copy of a node object
18 *
19 * \param[in] data Node copy (created by pe__copy_node()) to free
20 */
21 void
22 pcmk__free_node_copy(void *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 if (data != NULL) {
25 pcmk_node_t *node = data;
26
27 if (node->assign != NULL) {
28 // This is the only member allocated separately for a node copy
29 free(node->assign);
30 }
31 free(node);
32 }
33 }
34
35 /*!
36 * \internal
37 * \brief Check whether a node is online
38 *
39 * \param[in] node Node to check
40 *
41 * \return true if \p node is online, otherwise false
42 */
43 bool
44 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)
*/
45 {
46 return (node != NULL) && node->details->online;
47 }
48
49 /*!
50 * \internal
51 * \brief Check whether a node is pending
52 *
53 * Check whether a node is pending. A node is pending if it is a member of the
54 * cluster but not the controller group, which means it is in the process of
55 * either joining or leaving the cluster.
56 *
57 * \param[in] node Node to check
58 *
59 * \return true if \p node is pending, otherwise false
60 */
61 bool
62 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)
*/
63 {
64 return (node != NULL) && node->details->pending;
65 }
66
67 /*!
68 * \internal
69 * \brief Check whether a node is clean
70 *
71 * Check whether a node is clean. A node is clean if it is a cluster node or
72 * remote node that has been seen by the cluster at least once, or the
73 * startup-fencing cluster option is false; and the node, and its host if a
74 * guest or bundle node, are not scheduled to be fenced.
75 *
76 * \param[in] node Node to check
77 *
78 * \return true if \p node is clean, otherwise false
79 */
80 bool
81 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)
*/
82 {
83 return (node != NULL) && !(node->details->unclean);
84 }
85
86 /*!
87 * \internal
88 * \brief Check whether a node is shutting down
89 *
90 * \param[in] node Node to check
91 *
92 * \return true if \p node is shutting down, otherwise false
93 */
94 bool
95 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)
*/
96 {
97 return (node != NULL) && node->details->shutdown;
98 }
99
100 /*!
101 * \internal
102 * \brief Check whether a node is in maintenance mode
103 *
104 * \param[in] node Node to check
105 *
106 * \return true if \p node is in maintenance mode, otherwise false
107 */
108 bool
109 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)
*/
110 {
111 return (node != NULL) && node->details->maintenance;
112 }
113
114 /*!
115 * \internal
116 * \brief Call a function for each resource active on a node
117 *
118 * Call a caller-supplied function with a caller-supplied argument for each
119 * resource that is active on a given node. If the function returns false, this
120 * function will return immediately without processing any remaining resources.
121 *
122 * \param[in] node Node to check
123 *
124 * \return Result of last call of \p fn (or false if none)
125 */
126 bool
127 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)
*/
128 bool (*fn)(pcmk_resource_t *, void *),
129 void *user_data)
130 {
131 bool result = false;
132
133 if ((node != NULL) && (fn != NULL)) {
134 for (GList *item = node->details->running_rsc; item != NULL;
135 item = item->next) {
136
137 result = fn((pcmk_resource_t *) item->data, user_data);
138 if (!result) {
139 break;
140 }
141 }
142 }
143 return result;
144 }
145
146 void
147 pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid)
/* ![[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)
*/
148 {
149 pcmk__assert(xml != NULL);
150
151 if (node != NULL) {
152 crm_xml_add(xml, PCMK__XA_ATTR_HOST, node);
153 }
154
155 if (nodeid > 0) {
156 crm_xml_add_int(xml, PCMK__XA_ATTR_HOST_ID, nodeid);
157 }
158 }
159
160 /*!
161 * \internal
162 * \brief Find a node by name in a list of nodes
163 *
164 * \param[in] nodes List of nodes (as pcmk_node_t*)
165 * \param[in] node_name Name of node to find
166 *
167 * \return Node from \p nodes that matches \p node_name if any, otherwise NULL
168 */
169 pcmk_node_t *
170 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)
*/
171 {
172 if (node_name != NULL) {
173 for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
174 pcmk_node_t *node = (pcmk_node_t *) iter->data;
175
176 if (pcmk__str_eq(node->priv->name, node_name, pcmk__str_casei)) {
177 return node;
178 }
179 }
180 }
181 return NULL;
182 }
183
184 #define XP_SHUTDOWN "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_UNAME "='%s']/" \
185 PCMK__XE_TRANSIENT_ATTRIBUTES "/" PCMK_XE_INSTANCE_ATTRIBUTES "/" \
186 PCMK_XE_NVPAIR "[@" PCMK_XA_NAME "='" PCMK__NODE_ATTR_SHUTDOWN "']"
187
188 /*!
189 * \brief Get value of a node's shutdown attribute from CIB, if present
190 *
191 * \param[in] cib CIB to check
192 * \param[in] node Name of node to check
193 *
194 * \return Value of shutdown attribute for \p node in \p cib if any,
195 * otherwise NULL
196 * \note The return value is a pointer into \p cib and so is valid only for the
197 * lifetime of that object.
198 */
199 const char *
200 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)
*/
201 {
202 if ((cib != NULL) && (node != NULL)) {
203 char *xpath = crm_strdup_printf(XP_SHUTDOWN, node);
204 xmlNode *match = get_xpath_object(xpath, cib, LOG_TRACE);
205
206 free(xpath);
207 if (match != NULL) {
208 return crm_element_value(match, PCMK_XA_VALUE);
209 }
210 }
211 return NULL;
212 }