1 /*
2 * Copyright 2004-2026 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 #include <stdint.h> // uint32_t
14 #include <errno.h> // EINVAL
15 #include <glib.h> // gboolean, FALSE, etc.
16 #include <libxml/tree.h> // xmlNode
17
18 #include <crm/common/scheduler.h>
19
20 uint32_t pcmk__warnings = 0;
21
22 /*!
23 * \brief Create a new object to hold scheduler data
24 *
25 * \return New, initialized scheduler data, or NULL on memory error
26 * \note Only pcmk_scheduler_t objects created with this function (as opposed
27 * to statically declared or directly allocated) should be used with the
28 * functions in this library, to allow for future extensions to the
29 * data type. The caller is responsible for freeing the memory with
30 * pcmk_free_scheduler() when the instance is no longer needed.
31 */
32 pcmk_scheduler_t *
33 pcmk_new_scheduler(void)
34 {
35 pcmk_scheduler_t *scheduler = calloc(1, sizeof(pcmk_scheduler_t));
36
37 if (scheduler == NULL) {
38 return NULL;
39 }
40 scheduler->priv = calloc(1, sizeof(pcmk__scheduler_private_t));
41 if (scheduler->priv == NULL) {
42 free(scheduler);
43 return NULL;
44 }
45 pcmk__set_scheduler_defaults(scheduler);
46 return scheduler;
47 }
48
49 /*!
50 * \internal
51 * \brief Set non-zero default values in scheduler data
52 *
53 * \param[in,out] scheduler Scheduler data to modify
54 *
55 * \note Values that default to NULL or 0 will remain unchanged
56 */
57 void
58 pcmk__set_scheduler_defaults(pcmk_scheduler_t *scheduler)
59 {
60 pcmk__assert(scheduler != NULL);
61 scheduler->flags = 0U;
62 #if PCMK__CONCURRENT_FENCING_DEFAULT_TRUE
63 pcmk__set_scheduler_flags(scheduler,
64 pcmk__sched_symmetric_cluster
65 |pcmk__sched_concurrent_fencing
66 |pcmk__sched_stop_removed_resources
67 |pcmk__sched_cancel_removed_actions);
68 #else
69 pcmk__set_scheduler_flags(scheduler,
70 pcmk__sched_symmetric_cluster
71 |pcmk__sched_stop_removed_resources
72 |pcmk__sched_cancel_removed_actions);
73 #endif
74 scheduler->no_quorum_policy = pcmk_no_quorum_stop;
75 scheduler->priv->next_action_id = 1;
76 scheduler->priv->next_ordering_id = 1;
77 }
78
79 /*!
80 * \brief Reset scheduler data to defaults
81 *
82 * Free scheduler data except the local node name and output object, and reset
83 * all other values to defaults, so the data is suitable for rerunning status
84 *
85 * \param[in,out] scheduler Scheduler data to reset
86 */
87 void
88 pcmk_reset_scheduler(pcmk_scheduler_t *scheduler)
89 {
|
(1) Event path: |
Condition "scheduler == NULL", taking false branch. |
90 if (scheduler == NULL) {
91 return;
92 }
93
94 /* Be careful about the order of freeing members. Many contain references to
95 * other members that will become dangling if those members are freed first.
96 * For example, the node name and ID of Pacemaker Remote nodes are pointers
97 * into resource objects. Ensure that earlier-freed members are not needed
98 * by any of the free functions for later-freed members.
99 */
100
101 scheduler->dc_node = NULL;
102
103 g_list_free_full(scheduler->nodes, pcmk__free_node);
104 scheduler->nodes = NULL;
105
106 // Do not reset local_node_name or out
107
|
(2) Event path: |
Condition "_p", taking true branch. |
108 g_clear_pointer(&scheduler->priv->now, crm_time_free);
|
(3) Event path: |
Condition "_p", taking true branch. |
109 g_clear_pointer(&scheduler->priv->options, g_hash_table_destroy);
110
111 scheduler->priv->fence_action = NULL;
112 scheduler->priv->fence_timeout_ms = 0U;
113 scheduler->priv->priority_fencing_ms = 0U;
114 scheduler->priv->shutdown_lock_ms = 0U;
115 scheduler->priv->node_pending_ms = 0U;
116 scheduler->priv->placement_strategy = NULL;
117 scheduler->priv->rsc_defaults = NULL;
118 scheduler->priv->op_defaults = NULL;
119
120 g_list_free_full(scheduler->priv->resources, pcmk__free_resource);
121 scheduler->priv->resources = NULL;
122
|
(4) Event path: |
Condition "_p", taking true branch. |
123 g_clear_pointer(&scheduler->priv->templates, g_hash_table_destroy);
|
(5) Event path: |
Condition "_p", taking true branch. |
124 g_clear_pointer(&scheduler->priv->tags, g_hash_table_destroy);
125
126 g_list_free_full(scheduler->priv->actions, pcmk__free_action);
127 scheduler->priv->actions = NULL;
128
|
(6) Event path: |
Condition "_p", taking true branch. |
129 g_clear_pointer(&scheduler->priv->singletons, g_hash_table_destroy);
|
CID (unavailable; MK=8cbdd523287e02a2db6daa7153e1d849) (#6 of 10): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(7) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(8) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
130 g_clear_pointer(&scheduler->priv->failed, pcmk__xml_free);
131
132 pcmk__free_param_checks(scheduler);
133
134 g_clear_pointer(&scheduler->priv->stop_needed, g_list_free);
135
136 g_list_free_full(scheduler->priv->location_constraints,
137 pcmk__free_location);
138 scheduler->priv->location_constraints = NULL;
139
140 g_list_free_full(scheduler->priv->colocation_constraints, free);
141 scheduler->priv->colocation_constraints = NULL;
142
143 g_list_free_full(scheduler->priv->ordering_constraints,
144 pcmk__free_action_relation);
145 scheduler->priv->ordering_constraints = NULL;
146
147 g_clear_pointer(&scheduler->priv->ticket_constraints, g_hash_table_destroy);
148
149 scheduler->priv->ninstances = 0;
150 scheduler->priv->blocked_resources = 0;
151 scheduler->priv->disabled_resources = 0;
152 scheduler->priv->recheck_by = 0;
153
154 g_clear_pointer(&scheduler->priv->graph, pcmk__xml_free);
155
156 scheduler->priv->synapse_count = 0;
157
158 g_clear_pointer(&scheduler->input, pcmk__xml_free);
159
160 pcmk__set_scheduler_defaults(scheduler);
161
162 pcmk__config_has_error = false;
163 pcmk__config_has_warning = false;
164 }
165
166 /*!
167 * \brief Free scheduler data
168 *
169 * \param[in,out] scheduler Scheduler data to free
170 */
171 void
172 pcmk_free_scheduler(pcmk_scheduler_t *scheduler)
173 {
174 if (scheduler != NULL) {
175 pcmk_reset_scheduler(scheduler);
176 free(scheduler->priv->local_node_name);
177 free(scheduler->priv);
178 free(scheduler);
179 }
180 }
181
182 /*!
183 * \internal
184 * \brief Get the Designated Controller node from scheduler data
185 *
186 * \param[in] scheduler Scheduler data
187 *
188 * \return Designated Controller node from scheduler data, or NULL if none
189 */
190 pcmk_node_t *
191 pcmk_get_dc(const pcmk_scheduler_t *scheduler)
192 {
193 return (scheduler == NULL)? NULL : scheduler->dc_node;
194 }
195
196 /*!
197 * \internal
198 * \brief Get the no quorum policy from scheduler data
199 *
200 * \param[in] scheduler Scheduler data
201 *
202 * \return No quorum policy from scheduler data
203 */
204 enum pe_quorum_policy
205 pcmk_get_no_quorum_policy(const pcmk_scheduler_t *scheduler)
206 {
207 if (scheduler == NULL) {
208 return pcmk_no_quorum_stop; // The default
209 }
210 return scheduler->no_quorum_policy;
211 }
212
213 /*!
214 * \internal
215 * \brief Set CIB XML as scheduler input in scheduler data
216 *
217 * \param[out] scheduler Scheduler data
218 * \param[in] cib CIB XML to set as scheduler input
219 *
220 * \return Standard Pacemaker return code (EINVAL if \p scheduler is NULL,
221 * otherwise pcmk_rc_ok)
222 * \note This will not free any previously set scheduler CIB.
223 */
224 int
225 pcmk_set_scheduler_cib(pcmk_scheduler_t *scheduler, xmlNode *cib)
226 {
227 if (scheduler == NULL) {
228 return EINVAL;
229 }
230 scheduler->input = cib;
231 return pcmk_rc_ok;
232 }
233
234 /*!
235 * \internal
236 * \brief Check whether cluster has quorum
237 *
238 * \param[in] scheduler Scheduler data
239 *
240 * \return true if cluster has quorum, otherwise false
241 */
242 bool
243 pcmk_has_quorum(const pcmk_scheduler_t *scheduler)
244 {
245 if (scheduler == NULL) {
246 return false;
247 }
248 return pcmk__is_set(scheduler->flags, pcmk__sched_quorate);
249 }
250
251 /*!
252 * \brief Find a node by name in scheduler data
253 *
254 * \param[in] scheduler Scheduler data
255 * \param[in] node_name Name of node to find
256 *
257 * \return Node from scheduler data that matches \p node_name if any,
258 * otherwise NULL
259 */
260 pcmk_node_t *
261 pcmk_find_node(const pcmk_scheduler_t *scheduler, const char *node_name)
262 {
263 if ((scheduler == NULL) || (node_name == NULL)) {
264 return NULL;
265 }
266 return pcmk__find_node_in_list(scheduler->nodes, node_name);
267 }
268
269 /*!
270 * \internal
271 * \brief Get scheduler data's "now" in epoch time
272 *
273 * \param[in,out] scheduler Scheduler data
274 *
275 * \return Scheduler data's "now" as seconds since epoch (defaulting to current
276 * time)
277 */
278 time_t
279 pcmk__scheduler_epoch_time(pcmk_scheduler_t *scheduler)
280 {
281 if (scheduler == NULL) {
282 return time(NULL);
283 }
284 if (scheduler->priv->now == NULL) {
285 pcmk__trace("Scheduler 'now' set to current time");
286 scheduler->priv->now = crm_time_new(NULL);
287 }
288 return crm_time_get_seconds_since_epoch(scheduler->priv->now);
289 }
290
291 /*!
292 * \internal
293 * \brief Update "recheck by" time in scheduler data
294 *
295 * \param[in] recheck Epoch time when recheck should happen
296 * \param[in,out] scheduler Scheduler data
297 * \param[in] reason What time is being updated for (for logs)
298 */
299 void
300 pcmk__update_recheck_time(time_t recheck, pcmk_scheduler_t *scheduler,
301 const char *reason)
302 {
303 pcmk__assert(scheduler != NULL);
304
305 if ((recheck > pcmk__scheduler_epoch_time(scheduler))
306 && ((scheduler->priv->recheck_by == 0)
307 || (scheduler->priv->recheck_by > recheck))) {
308
309 scheduler->priv->recheck_by = recheck;
310 pcmk__debug("Updated next scheduler recheck to %s for %s",
311 g_strchomp(ctime(&recheck)),
312 pcmk__s(reason, "some reason"));
313 }
314 }
315
316 /* Fail count clearing for parameter changes normally happens when unpacking
317 * history, before resources are unpacked. However, for bundles using the
318 * REMOTE_CONTAINER_HACK, we can't check the conditions until after unpacking
319 * the bundle, so those parameter checks are deferred using the APIs below.
320 */
321
322 // History entry to be checked later for fail count clearing
323 struct param_check {
324 const xmlNode *rsc_history; // History entry XML
325 pcmk_resource_t *rsc; // Resource corresponding to history entry
326 pcmk_node_t *node; // Node corresponding to history entry
327 enum pcmk__check_parameters check_type; // What needs checking
328 };
329
330 /*!
331 * \internal
332 * \brief Add a deferred parameter check
333 *
334 * \param[in] rsc_history Resource history XML to check later
335 * \param[in,out] rsc Resource that history is for
336 * \param[in] node Node that history is for
337 * \param[in] flag What needs to be checked later
338 */
339 void
340 pcmk__add_param_check(const xmlNode *rsc_history, pcmk_resource_t *rsc,
341 pcmk_node_t *node, enum pcmk__check_parameters flag)
342 {
343 struct param_check *param_check = NULL;
344
345 CRM_CHECK((rsc_history != NULL) && (rsc != NULL) && (node != NULL), return);
346
347 pcmk__trace("Deferring checks of %s until after assignment",
348 pcmk__xe_id(rsc_history));
349 param_check = pcmk__assert_alloc(1, sizeof(struct param_check));
350 param_check->rsc_history = rsc_history;
351 param_check->rsc = rsc;
352 param_check->node = node;
353 param_check->check_type = flag;
354
355 rsc->priv->scheduler->priv->param_check =
356 g_list_prepend(rsc->priv->scheduler->priv->param_check, param_check);
357 }
358
359 /*!
360 * \internal
361 * \brief Call a function for each deferred parameter check
362 *
363 * \param[in,out] scheduler Scheduler data
364 * \param[in] cb Function to be called
365 */
366 void
367 pcmk__foreach_param_check(pcmk_scheduler_t *scheduler,
368 void (*cb)(pcmk_resource_t*, pcmk_node_t*,
369 const xmlNode*,
370 enum pcmk__check_parameters))
371 {
372 CRM_CHECK((scheduler != NULL) && (cb != NULL), return);
373
374 for (GList *item = scheduler->priv->param_check;
375 item != NULL; item = item->next) {
376 struct param_check *param_check = item->data;
377
378 cb(param_check->rsc, param_check->node, param_check->rsc_history,
379 param_check->check_type);
380 }
381 }
382
383 /*!
384 * \internal
385 * \brief Free all deferred parameter checks
386 *
387 * \param[in,out] scheduler Scheduler data
388 */
389 void
390 pcmk__free_param_checks(pcmk_scheduler_t *scheduler)
391 {
392 if (scheduler == NULL) {
393 return;
394 }
395
396 g_list_free_full(scheduler->priv->param_check, free);
397 scheduler->priv->param_check = NULL;
398 }
399