Coverage Report

Created: 2025-12-31 06:13

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