Coverage Report

Created: 2025-11-26 06:09

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 <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
}