Line data Source code
1 : /*
2 : * Copyright 2021-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 General Public License version 2
7 : * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 : */
9 :
10 : #include <crm_internal.h>
11 :
12 : #include <errno.h>
13 : #include <glib.h>
14 : #include <libxml/tree.h>
15 :
16 : #include <crm/cib/internal.h>
17 : #include <crm/common/mainloop.h>
18 : #include <crm/common/results.h>
19 : #include <crm/common/output_internal.h>
20 : #include <crm/pengine/internal.h>
21 :
22 : #include <pacemaker.h>
23 : #include <pacemaker-internal.h>
24 :
25 : // Search path for resource operation history (takes node name and resource ID)
26 : #define XPATH_OP_HISTORY "//" PCMK_XE_STATUS \
27 : "/" PCMK__XE_NODE_STATE \
28 : "[@" PCMK_XA_UNAME "='%s']" \
29 : "/" PCMK__XE_LRM "/" PCMK__XE_LRM_RESOURCES \
30 : "/" PCMK__XE_LRM_RESOURCE "[@" PCMK_XA_ID "='%s']"
31 :
32 : static xmlNode *
33 0 : best_op(const pcmk_resource_t *rsc, const pcmk_node_t *node)
34 : {
35 0 : char *xpath = NULL;
36 0 : xmlNode *history = NULL;
37 0 : xmlNode *best = NULL;
38 0 : bool best_effective_op = false;
39 0 : guint best_interval = 0;
40 0 : bool best_failure = false;
41 0 : const char *best_digest = NULL;
42 :
43 : // Find node's resource history
44 0 : xpath = crm_strdup_printf(XPATH_OP_HISTORY, node->details->uname, rsc->id);
45 0 : history = get_xpath_object(xpath, rsc->cluster->input, LOG_NEVER);
46 0 : free(xpath);
47 :
48 : // Examine each history entry
49 0 : for (xmlNode *lrm_rsc_op = pcmk__xe_first_child(history,
50 : PCMK__XE_LRM_RSC_OP, NULL,
51 : NULL);
52 0 : lrm_rsc_op != NULL; lrm_rsc_op = pcmk__xe_next_same(lrm_rsc_op)) {
53 :
54 0 : const char *digest = crm_element_value(lrm_rsc_op,
55 : PCMK__XA_OP_RESTART_DIGEST);
56 0 : guint interval_ms = 0;
57 0 : const char *task = crm_element_value(lrm_rsc_op, PCMK_XA_OPERATION);
58 0 : bool effective_op = false;
59 0 : bool failure = pcmk__ends_with(pcmk__xe_id(lrm_rsc_op),
60 : "_last_failure_0");
61 :
62 :
63 0 : crm_element_value_ms(lrm_rsc_op, PCMK_META_INTERVAL, &interval_ms);
64 0 : effective_op = interval_ms == 0
65 0 : && pcmk__strcase_any_of(task, PCMK_ACTION_MONITOR,
66 : PCMK_ACTION_START,
67 : PCMK_ACTION_PROMOTE,
68 : PCMK_ACTION_MIGRATE_FROM, NULL);
69 :
70 0 : if (best == NULL) {
71 0 : goto is_best;
72 : }
73 :
74 0 : if (best_effective_op) {
75 : // Do not use an ineffective op if there's an effective one.
76 0 : if (!effective_op) {
77 0 : continue;
78 : }
79 : // Do not use an ineffective non-recurring op if there's a recurring one
80 0 : } else if (best_interval != 0
81 0 : && !effective_op
82 0 : && interval_ms == 0) {
83 0 : continue;
84 : }
85 :
86 : // Do not use last failure if there's a successful one.
87 0 : if (!best_failure && failure) {
88 0 : continue;
89 : }
90 :
91 : // Do not use an op without a restart digest if there's one with.
92 0 : if (best_digest != NULL && digest == NULL) {
93 0 : continue;
94 : }
95 :
96 : // Do not use an older op if there's a newer one.
97 0 : if (pe__is_newer_op(best, lrm_rsc_op, true) > 0) {
98 0 : continue;
99 : }
100 :
101 0 : is_best:
102 0 : best = lrm_rsc_op;
103 0 : best_effective_op = effective_op;
104 0 : best_interval = interval_ms;
105 0 : best_failure = failure;
106 0 : best_digest = digest;
107 : }
108 0 : return best;
109 : }
110 :
111 : /*!
112 : * \internal
113 : * \brief Remove a resource
114 : *
115 : * \param[in,out] cib An open connection to the CIB
116 : * \param[in] cib_opts Options to use in the CIB operation call
117 : * \param[in] rsc_id Resource to remove
118 : * \param[in] rsc_type Type of the resource ("primitive", "group", etc.)
119 : *
120 : * \return Standard Pacemaker return code
121 : */
122 : int
123 5 : pcmk__resource_delete(cib_t *cib, uint32_t cib_opts, const char *rsc_id,
124 : const char *rsc_type)
125 : {
126 5 : int rc = pcmk_rc_ok;
127 5 : xmlNode *msg_data = NULL;
128 :
129 5 : if (cib == NULL) {
130 0 : return ENOTCONN;
131 : }
132 :
133 5 : if (rsc_id == NULL || rsc_type == NULL) {
134 2 : return EINVAL;
135 : }
136 :
137 3 : msg_data = pcmk__xe_create(NULL, rsc_type);
138 3 : crm_xml_add(msg_data, PCMK_XA_ID, rsc_id);
139 :
140 3 : rc = cib->cmds->remove(cib, PCMK_XE_RESOURCES, msg_data, cib_opts);
141 3 : rc = pcmk_legacy2rc(rc);
142 :
143 3 : free_xml(msg_data);
144 3 : return rc;
145 : }
146 :
147 : int
148 6 : pcmk_resource_delete(xmlNodePtr *xml, const char *rsc_id, const char *rsc_type)
149 : {
150 6 : pcmk__output_t *out = NULL;
151 6 : int rc = pcmk_rc_ok;
152 6 : uint32_t cib_opts = cib_sync_call;
153 6 : cib_t *cib = NULL;
154 :
155 6 : rc = pcmk__xml_output_new(&out, xml);
156 6 : if (rc != pcmk_rc_ok) {
157 0 : return rc;
158 : }
159 :
160 6 : cib = cib_new();
161 6 : if (cib == NULL) {
162 0 : rc = pcmk_rc_cib_corrupt;
163 0 : goto done;
164 : }
165 :
166 6 : rc = cib->cmds->signon(cib, crm_system_name, cib_command);
167 6 : rc = pcmk_legacy2rc(rc);
168 :
169 6 : if (rc != pcmk_rc_ok) {
170 1 : goto done;
171 : }
172 :
173 5 : rc = pcmk__resource_delete(cib, cib_opts, rsc_id, rsc_type);
174 :
175 6 : done:
176 6 : if (cib != NULL) {
177 6 : cib__clean_up_connection(&cib);
178 : }
179 :
180 6 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
181 6 : return rc;
182 : }
183 :
184 : /*!
185 : * \internal
186 : * \brief Calculate and output resource operation digests
187 : *
188 : * \param[in,out] out Output object
189 : * \param[in,out] rsc Resource to calculate digests for
190 : * \param[in] node Node whose operation history should be used
191 : * \param[in] overrides Hash table of configuration parameters to override
192 : *
193 : * \return Standard Pacemaker return code
194 : */
195 : int
196 0 : pcmk__resource_digests(pcmk__output_t *out, pcmk_resource_t *rsc,
197 : const pcmk_node_t *node, GHashTable *overrides)
198 : {
199 0 : const char *task = NULL;
200 0 : xmlNode *xml_op = NULL;
201 0 : pcmk__op_digest_t *digests = NULL;
202 0 : guint interval_ms = 0;
203 0 : int rc = pcmk_rc_ok;
204 :
205 0 : if ((out == NULL) || (rsc == NULL) || (node == NULL)) {
206 0 : return EINVAL;
207 : }
208 0 : if (rsc->variant != pcmk_rsc_variant_primitive) {
209 : // Only primitives get operation digests
210 0 : return EOPNOTSUPP;
211 : }
212 :
213 : // Find XML of operation history to use
214 0 : xml_op = best_op(rsc, node);
215 :
216 : // Generate an operation key
217 0 : if (xml_op != NULL) {
218 0 : task = crm_element_value(xml_op, PCMK_XA_OPERATION);
219 0 : crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
220 : }
221 0 : if (task == NULL) { // Assume start if no history is available
222 0 : task = PCMK_ACTION_START;
223 0 : interval_ms = 0;
224 : }
225 :
226 : // Calculate and show digests
227 0 : digests = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op,
228 : overrides, true, rsc->cluster);
229 0 : rc = out->message(out, "digests", rsc, node, task, interval_ms, digests);
230 :
231 0 : pe__free_digests(digests);
232 0 : return rc;
233 : }
234 :
235 : // @COMPAT The scheduler parameter is unused and can be removed at the next break
236 : int
237 0 : pcmk_resource_digests(xmlNodePtr *xml, pcmk_resource_t *rsc,
238 : const pcmk_node_t *node, GHashTable *overrides,
239 : pcmk_scheduler_t *scheduler)
240 : {
241 0 : pcmk__output_t *out = NULL;
242 0 : int rc = pcmk_rc_ok;
243 :
244 0 : rc = pcmk__xml_output_new(&out, xml);
245 0 : if (rc != pcmk_rc_ok) {
246 0 : return rc;
247 : }
248 0 : pcmk__register_lib_messages(out);
249 0 : rc = pcmk__resource_digests(out, rsc, node, overrides);
250 0 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
251 0 : return rc;
252 : }
|