This source file includes following definitions.
- node_has_attr
- remove_topology_level
- register_fencing_topology
- fencing_topology_init
- update_stonith_watchdog_timeout_ms
- mark_dirty_if_cib_registered
- device_is_dirty
- cib_devices_update
- update_cib_stonith_devices
- watchdog_device_update
- fenced_query_cib
- update_fencing_topology
- update_cib_cache_cb
- init_cib_cache_cb
- cib_connection_destroy
- fenced_cib_cleanup
- setup_cib
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <libxml/tree.h>
15 #include <libxml/xpath.h>
16
17 #include <crm/crm.h>
18 #include <crm/common/xml.h>
19
20 #include <crm/cluster/internal.h>
21
22 #include <crm/cib.h>
23 #include <crm/cib/internal.h>
24
25 #include <pacemaker-fenced.h>
26
27 static xmlNode *local_cib = NULL;
28 static cib_t *cib_api = NULL;
29 static bool have_cib_devices = FALSE;
30
31
32
33
34
35
36
37
38
39
40
41 gboolean
42 node_has_attr(const char *node, const char *name, const char *value)
43 {
44 GString *xpath = NULL;
45 xmlNode *match;
46
47 CRM_CHECK((local_cib != NULL) && (node != NULL) && (name != NULL)
48 && (value != NULL), return FALSE);
49
50
51
52
53
54
55 xpath = g_string_sized_new(256);
56 pcmk__g_strcat(xpath,
57 "//" PCMK_XE_NODES "/" PCMK_XE_NODE
58 "[@" PCMK_XA_UNAME "='", node, "']"
59 "/" PCMK_XE_INSTANCE_ATTRIBUTES
60 "/" PCMK_XE_NVPAIR
61 "[@" PCMK_XA_NAME "='", name, "' "
62 "and @" PCMK_XA_VALUE "='", value, "']", NULL);
63
64 match = pcmk__xpath_find_one(local_cib->doc, xpath->str, LOG_NEVER);
65
66 g_string_free(xpath, TRUE);
67 return (match != NULL);
68 }
69
70 static void
71 remove_topology_level(xmlNode *match)
72 {
73 int index = 0;
74 char *key = NULL;
75 xmlNode *data = NULL;
76
77 CRM_CHECK(match != NULL, return);
78
79 key = stonith_level_key(match, fenced_target_by_unknown);
80 crm_element_value_int(match, PCMK_XA_INDEX, &index);
81
82 data = pcmk__xe_create(NULL, PCMK_XE_FENCING_LEVEL);
83 crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
84 crm_xml_add(data, PCMK_XA_TARGET, key);
85 crm_xml_add_int(data, PCMK_XA_INDEX, index);
86
87 fenced_unregister_level(data, NULL);
88
89 free(key);
90 pcmk__xml_free(data);
91 }
92
93 static void
94 register_fencing_topology(xmlXPathObjectPtr xpathObj)
95 {
96 int max = pcmk__xpath_num_results(xpathObj);
97
98 for (int lpc = 0; lpc < max; lpc++) {
99 xmlNode *match = pcmk__xpath_result(xpathObj, lpc);
100
101 if (match == NULL) {
102 continue;
103 }
104 remove_topology_level(match);
105 fenced_register_level(match, NULL);
106 }
107 }
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 void
129 fencing_topology_init(void)
130 {
131 xmlXPathObject *xpathObj = NULL;
132 const char *xpath = "//" PCMK_XE_FENCING_LEVEL;
133
134 crm_trace("Full topology refresh");
135 free_topology_list();
136 init_topology_list();
137
138
139 xpathObj = pcmk__xpath_search(local_cib->doc, xpath);
140 register_fencing_topology(xpathObj);
141
142 xmlXPathFreeObject(xpathObj);
143 }
144
145 #define XPATH_WATCHDOG_TIMEOUT "//" PCMK_XE_NVPAIR \
146 "[@" PCMK_XA_NAME "='" \
147 PCMK_OPT_STONITH_WATCHDOG_TIMEOUT "']"
148
149 static void
150 update_stonith_watchdog_timeout_ms(xmlNode *cib)
151 {
152 long long timeout_ms = 0;
153 xmlNode *stonith_watchdog_xml = NULL;
154 const char *value = NULL;
155
156
157 stonith_watchdog_xml = pcmk__xpath_find_one(cib->doc,
158 XPATH_WATCHDOG_TIMEOUT,
159 LOG_NEVER);
160 if (stonith_watchdog_xml) {
161 value = crm_element_value(stonith_watchdog_xml, PCMK_XA_VALUE);
162 }
163 if (value) {
164 timeout_ms = crm_get_msec(value);
165 }
166
167 if (timeout_ms < 0) {
168 timeout_ms = pcmk__auto_stonith_watchdog_timeout();
169 }
170
171 stonith_watchdog_timeout_ms = timeout_ms;
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185 static void
186 mark_dirty_if_cib_registered(gpointer key, gpointer value, gpointer user_data)
187 {
188 fenced_device_t *device = value;
189
190 if (pcmk_is_set(device->flags, fenced_df_cib_registered)) {
191 fenced_device_set_flags(device, fenced_df_dirty);
192 }
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208 static gboolean
209 device_is_dirty(gpointer key, gpointer value, gpointer user_data)
210 {
211 fenced_device_t *device = value;
212
213 return pcmk_is_set(device->flags, fenced_df_dirty);
214 }
215
216
217
218
219
220 static void
221 cib_devices_update(void)
222 {
223 crm_info("Updating devices to version %s.%s.%s",
224 crm_element_value(local_cib, PCMK_XA_ADMIN_EPOCH),
225 crm_element_value(local_cib, PCMK_XA_EPOCH),
226 crm_element_value(local_cib, PCMK_XA_NUM_UPDATES));
227
228 fenced_foreach_device(mark_dirty_if_cib_registered, NULL);
229
230
231
232
233 g_list_free_full(stonith_watchdog_targets, free);
234 stonith_watchdog_targets = NULL;
235
236 fenced_scheduler_run(local_cib);
237
238 fenced_foreach_device_remove(device_is_dirty);
239 }
240
241 #define PRIMITIVE_ID_XP_FRAGMENT "/" PCMK_XE_PRIMITIVE "[@" PCMK_XA_ID "='"
242
243 static void
244 update_cib_stonith_devices(const xmlNode *patchset)
245 {
246 char *reason = NULL;
247
248 for (const xmlNode *change = pcmk__xe_first_child(patchset, NULL, NULL,
249 NULL);
250 change != NULL; change = pcmk__xe_next(change, NULL)) {
251
252 const char *op = crm_element_value(change, PCMK_XA_OPERATION);
253 const char *xpath = crm_element_value(change, PCMK_XA_PATH);
254 const char *primitive_xpath = NULL;
255
256 if (pcmk__str_eq(op, PCMK_VALUE_MOVE, pcmk__str_null_matches)
257 || (strstr(xpath, "/" PCMK_XE_STATUS) != NULL)) {
258 continue;
259 }
260
261 primitive_xpath = strstr(xpath, PRIMITIVE_ID_XP_FRAGMENT);
262 if ((primitive_xpath != NULL)
263 && pcmk__str_eq(op, PCMK_VALUE_DELETE, pcmk__str_none)) {
264
265 const char *rsc_id = NULL;
266 const char *end_quote = NULL;
267
268 if ((strstr(primitive_xpath, PCMK_XE_INSTANCE_ATTRIBUTES) != NULL)
269 || (strstr(primitive_xpath, PCMK_XE_META_ATTRIBUTES) != NULL)) {
270
271 reason = pcmk__str_copy("(meta) attribute deleted from "
272 "resource");
273 break;
274 }
275
276 rsc_id = primitive_xpath + sizeof(PRIMITIVE_ID_XP_FRAGMENT) - 1;
277 end_quote = strchr(rsc_id, '\'');
278
279 CRM_LOG_ASSERT(end_quote != NULL);
280 if (end_quote == NULL) {
281 crm_err("Bug: Malformed item in Pacemaker-generated patchset");
282 continue;
283 }
284
285 if (strchr(end_quote, '/') == NULL) {
286
287
288
289
290 char *copy = strndup(rsc_id, end_quote - rsc_id);
291
292 pcmk__assert(copy != NULL);
293 stonith_device_remove(copy, true);
294
295
296
297
298 free(copy);
299 continue;
300 }
301 }
302
303 if (strstr(xpath, "/" PCMK_XE_RESOURCES)
304 || strstr(xpath, "/" PCMK_XE_CONSTRAINTS)
305 || strstr(xpath, "/" PCMK_XE_RSC_DEFAULTS)) {
306
307 const char *shortpath = strrchr(xpath, '/');
308
309 reason = crm_strdup_printf("%s %s", op, shortpath + 1);
310 break;
311 }
312 }
313
314 if (reason != NULL) {
315 crm_info("Updating device list from CIB: %s", reason);
316 cib_devices_update();
317 free(reason);
318 } else {
319 crm_trace("No updates for device list found in CIB");
320 }
321 }
322
323 static void
324 watchdog_device_update(void)
325 {
326 if (stonith_watchdog_timeout_ms > 0) {
327 if (!fenced_has_watchdog_device()
328 && (stonith_watchdog_targets == NULL)) {
329
330
331
332 int rc;
333 xmlNode *xml;
334
335 xml = create_device_registration_xml(
336 STONITH_WATCHDOG_ID,
337 st_namespace_internal,
338 STONITH_WATCHDOG_AGENT,
339 NULL,
340
341
342
343 NULL);
344 rc = fenced_device_register(xml, true);
345 pcmk__xml_free(xml);
346 if (rc != pcmk_rc_ok) {
347 exit_code = CRM_EX_FATAL;
348 crm_crit("Cannot register watchdog pseudo fence agent: %s",
349 pcmk_rc_str(rc));
350 stonith_shutdown(0);
351 }
352 }
353
354 } else if (fenced_has_watchdog_device()) {
355
356 stonith_device_remove(STONITH_WATCHDOG_ID, true);
357 }
358 }
359
360
361
362
363
364
365
366 static int
367 fenced_query_cib(void)
368 {
369 int rc = pcmk_ok;
370
371 crm_trace("Re-requesting full CIB");
372 rc = cib_api->cmds->query(cib_api, NULL, &local_cib, cib_sync_call);
373 rc = pcmk_legacy2rc(rc);
374 if (rc == pcmk_rc_ok) {
375 pcmk__assert(local_cib != NULL);
376 } else {
377 crm_err("Couldn't retrieve the CIB: %s " QB_XS " rc=%d",
378 pcmk_rc_str(rc), rc);
379 }
380 return rc;
381 }
382
383 static void
384 update_fencing_topology(const char *event, xmlNode *msg)
385 {
386 xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT,
387 NULL, NULL);
388 xmlNode *patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
389
390 int format = 1;
391
392 int add[] = { 0, 0, 0 };
393 int del[] = { 0, 0, 0 };
394
395 CRM_CHECK(patchset != NULL, return);
396
397 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
398 if (format != 2) {
399 crm_warn("Unknown patch format: %d", format);
400 return;
401 }
402
403 xml_patch_versions(patchset, add, del);
404
405 for (xmlNode *change = pcmk__xe_first_child(patchset, NULL, NULL, NULL);
406 change != NULL; change = pcmk__xe_next(change, NULL)) {
407
408 const char *op = crm_element_value(change, PCMK_XA_OPERATION);
409 const char *xpath = crm_element_value(change, PCMK_XA_PATH);
410
411 if (op == NULL) {
412 continue;
413 }
414
415 if (strstr(xpath, "/" PCMK_XE_FENCING_LEVEL) != NULL) {
416
417 crm_trace("Handling %s operation %d.%d.%d for %s",
418 op, add[0], add[1], add[2], xpath);
419
420 if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
421
422
423
424 crm_info("Re-initializing fencing topology after %s operation "
425 "%d.%d.%d for %s",
426 op, add[0], add[1], add[2], xpath);
427 fencing_topology_init();
428 return;
429 }
430
431 if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
432 fenced_register_level(change->children, NULL);
433
434 } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
435 xmlNode *match = pcmk__xe_first_child(change,
436 PCMK_XE_CHANGE_RESULT,
437 NULL, NULL);
438
439 if (match != NULL) {
440 remove_topology_level(match->children);
441 fenced_register_level(match->children, NULL);
442 }
443 }
444 continue;
445 }
446
447 if (strstr(xpath, "/" PCMK_XE_FENCING_TOPOLOGY) != NULL) {
448
449 crm_info("Re-initializing fencing topology after top-level "
450 "%s operation %d.%d.%d for %s",
451 op, add[0], add[1], add[2], xpath);
452 fencing_topology_init();
453 return;
454 }
455
456 if ((strstr(xpath, "/" PCMK_XE_CONFIGURATION) != NULL)
457 && (pcmk__xe_first_child(change, PCMK_XE_FENCING_TOPOLOGY, NULL,
458 NULL) != NULL)
459 && pcmk__str_any_of(op, PCMK_VALUE_CREATE, PCMK_VALUE_DELETE,
460 NULL)) {
461
462
463 crm_info("Re-initializing fencing topology after top-level "
464 "%s operation %d.%d.%d for %s",
465 op, add[0], add[1], add[2], xpath);
466 fencing_topology_init();
467 return;
468 }
469
470 crm_trace("Nothing for us in %s operation %d.%d.%d for %s",
471 op, add[0], add[1], add[2], xpath);
472 }
473 }
474
475 static void
476 update_cib_cache_cb(const char *event, xmlNode * msg)
477 {
478 xmlNode *patchset = NULL;
479 long long timeout_ms_saved = stonith_watchdog_timeout_ms;
480 bool need_full_refresh = false;
481
482 if(!have_cib_devices) {
483 crm_trace("Skipping updates until we get a full dump");
484 return;
485
486 } else if(msg == NULL) {
487 crm_trace("Missing %s update", event);
488 return;
489 }
490
491
492
493
494 if (local_cib != NULL) {
495 int rc = pcmk_ok;
496 xmlNode *wrapper = NULL;
497
498 crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc);
499 if (rc != pcmk_ok) {
500 return;
501 }
502
503 wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, NULL,
504 NULL);
505 patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
506
507 rc = xml_apply_patchset(local_cib, patchset, TRUE);
508 switch (rc) {
509 case pcmk_ok:
510 case -pcmk_err_old_data:
511
512
513
514
515
516 break;
517 case -pcmk_err_diff_resync:
518 case -pcmk_err_diff_failed:
519 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
520 pcmk__xml_free(local_cib);
521 local_cib = NULL;
522 break;
523 default:
524 crm_warn("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
525 pcmk__xml_free(local_cib);
526 local_cib = NULL;
527 }
528 }
529
530 if (local_cib == NULL) {
531 if (fenced_query_cib() != pcmk_rc_ok) {
532 return;
533 }
534 need_full_refresh = true;
535 }
536
537 pcmk__refresh_node_caches_from_cib(local_cib);
538 update_stonith_watchdog_timeout_ms(local_cib);
539
540 if (timeout_ms_saved != stonith_watchdog_timeout_ms) {
541 need_full_refresh = true;
542 }
543
544 if (need_full_refresh) {
545 fencing_topology_init();
546 cib_devices_update();
547 } else {
548
549 update_fencing_topology(event, msg);
550 update_cib_stonith_devices(patchset);
551 }
552
553 watchdog_device_update();
554 }
555
556 static void
557 init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
558 {
559 crm_info("Updating device list from CIB");
560 have_cib_devices = TRUE;
561 local_cib = pcmk__xml_copy(NULL, output);
562
563 pcmk__refresh_node_caches_from_cib(local_cib);
564 update_stonith_watchdog_timeout_ms(local_cib);
565
566 fencing_topology_init();
567 cib_devices_update();
568 watchdog_device_update();
569 }
570
571 static void
572 cib_connection_destroy(gpointer user_data)
573 {
574 if (stonith_shutdown_flag) {
575 crm_info("Connection to the CIB manager closed");
576 return;
577 } else {
578 crm_crit("Lost connection to the CIB manager, shutting down");
579 }
580 if (cib_api) {
581 cib_api->cmds->signoff(cib_api);
582 }
583 stonith_shutdown(0);
584 }
585
586
587
588
589
590 void
591 fenced_cib_cleanup(void)
592 {
593 if (cib_api != NULL) {
594 cib_api->cmds->del_notify_callback(cib_api, PCMK__VALUE_CIB_DIFF_NOTIFY,
595 update_cib_cache_cb);
596 cib__clean_up_connection(&cib_api);
597 }
598 pcmk__xml_free(local_cib);
599 local_cib = NULL;
600 }
601
602 void
603 setup_cib(void)
604 {
605 int rc, retries = 0;
606
607 cib_api = cib_new();
608 if (cib_api == NULL) {
609 crm_err("No connection to the CIB manager");
610 return;
611 }
612
613 do {
614 sleep(retries);
615 rc = cib_api->cmds->signon(cib_api, crm_system_name, cib_command);
616 } while (rc == -ENOTCONN && ++retries < 5);
617
618 if (rc != pcmk_ok) {
619 crm_err("Could not connect to the CIB manager: %s (%d)", pcmk_strerror(rc), rc);
620 return;
621 }
622
623 rc = cib_api->cmds->add_notify_callback(cib_api,
624 PCMK__VALUE_CIB_DIFF_NOTIFY,
625 update_cib_cache_cb);
626 if (rc != pcmk_ok) {
627 crm_err("Could not set CIB notification callback");
628 return;
629 }
630
631 rc = cib_api->cmds->query(cib_api, NULL, NULL, cib_none);
632 cib_api->cmds->register_callback(cib_api, rc, 120, FALSE, NULL,
633 "init_cib_cache_cb", init_cib_cache_cb);
634 cib_api->cmds->set_connection_dnotify(cib_api, cib_connection_destroy);
635 crm_info("Watching for fencing topology changes");
636 }