This source file includes following definitions.
- dispatch_helper
- st_callback
- st_global_callback
- passive_test
- run_fence_failure_test
- run_fence_failure_rollover_test
- run_standard_test
- sanity_tests
- standard_dev_test
- mainloop_callback
- register_callback_helper
- test_async_fence_pass
- test_async_fence_custom_timeout
- test_async_fence_timeout
- test_async_monitor
- test_register_async_devices
- try_mainloop_connect
- iterate_mainloop_tests
- trigger_iterate_mainloop_tests
- test_shutdown
- mainloop_tests
- main
1
2
3
4
5
6
7
8 #include <crm_internal.h>
9
10 #include <sys/param.h>
11 #include <stdio.h>
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16 #include <sys/utsname.h>
17
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <fcntl.h>
21
22 #include <crm/crm.h>
23 #include <crm/msg_xml.h>
24 #include <crm/common/ipc.h>
25 #include <crm/cluster/internal.h>
26
27 #include <crm/stonith-ng.h>
28 #include <crm/fencing/internal.h>
29 #include <crm/common/xml.h>
30
31 #include <crm/common/mainloop.h>
32
33 static GMainLoop *mainloop = NULL;
34 static crm_trigger_t *trig = NULL;
35 static int mainloop_iter = 0;
36 static int callback_rc = 0;
37 typedef void (*mainloop_test_iteration_cb) (int check_event);
38
39 #define MAINLOOP_DEFAULT_TIMEOUT 2
40
41 #define mainloop_test_done(pass) \
42 if (pass) { \
43 crm_info("SUCCESS - %s", __func__); \
44 mainloop_iter++; \
45 mainloop_set_trigger(trig); \
46 } else { \
47 crm_err("FAILURE = %s async_callback %d", __func__, callback_rc); \
48 crm_exit(CRM_EX_ERROR); \
49 } \
50 callback_rc = 0; \
51
52
53 enum test_modes {
54 test_standard = 0,
55 test_passive,
56 test_api_sanity,
57 test_api_mainloop,
58 };
59
60 static pcmk__cli_option_t long_options[] = {
61
62 {
63 "verbose", no_argument, NULL, 'V',
64 NULL, pcmk__option_default
65 },
66 {
67 "version", no_argument, NULL, '$',
68 NULL, pcmk__option_default
69 },
70 {
71 "help", no_argument, NULL, '?',
72 NULL, pcmk__option_default
73 },
74 {
75 "passive", no_argument, NULL, 'p',
76 NULL, pcmk__option_default
77 },
78 {
79 "api_test", no_argument, NULL, 't',
80 NULL, pcmk__option_default
81 },
82 {
83 "mainloop_api_test", no_argument, NULL, 'm',
84 NULL, pcmk__option_default
85 },
86 { 0, 0, 0, 0 }
87 };
88
89 static stonith_t *st = NULL;
90 static struct pollfd pollfd;
91 static const int st_opts = st_opt_sync_call;
92 static int expected_notifications = 0;
93 static int verbose = 0;
94
95 static void
96 dispatch_helper(int timeout)
97 {
98 int rc;
99
100 crm_debug("Looking for notification");
101 pollfd.events = POLLIN;
102 while (true) {
103 rc = poll(&pollfd, 1, timeout);
104 if (rc > 0) {
105 if (!stonith_dispatch(st)) {
106 break;
107 }
108 } else {
109 break;
110 }
111 }
112 }
113
114 static void
115 st_callback(stonith_t * st, stonith_event_t * e)
116 {
117 if (st->state == stonith_disconnected) {
118 crm_exit(CRM_EX_DISCONNECT);
119 }
120
121 crm_notice("Operation %s requested by %s %s for peer %s. %s reported: %s (ref=%s)",
122 e->operation, e->origin, e->result == pcmk_ok ? "completed" : "failed",
123 e->target, e->executioner ? e->executioner : "<none>",
124 pcmk_strerror(e->result), e->id);
125
126 if (expected_notifications) {
127 expected_notifications--;
128 }
129 }
130
131 static void
132 st_global_callback(stonith_t * stonith, stonith_callback_data_t * data)
133 {
134 crm_notice("Call id %d completed with rc %d", data->call_id, data->rc);
135 }
136
137 static void
138 passive_test(void)
139 {
140 int rc = 0;
141
142 rc = st->cmds->connect(st, crm_system_name, &pollfd.fd);
143 if (rc != pcmk_ok) {
144 stonith_api_delete(st);
145 crm_exit(CRM_EX_DISCONNECT);
146 }
147 st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT, st_callback);
148 st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, st_callback);
149 st->cmds->register_notification(st, STONITH_OP_DEVICE_ADD, st_callback);
150 st->cmds->register_notification(st, STONITH_OP_DEVICE_DEL, st_callback);
151 st->cmds->register_callback(st, 0, 120, st_opt_timeout_updates, NULL, "st_global_callback",
152 st_global_callback);
153
154 dispatch_helper(600 * 1000);
155 }
156
157 #define single_test(cmd, str, num_notifications, expected_rc) \
158 { \
159 int rc = 0; \
160 rc = cmd; \
161 expected_notifications = 0; \
162 if (num_notifications) { \
163 expected_notifications = num_notifications; \
164 dispatch_helper(500); \
165 } \
166 if (rc != expected_rc) { \
167 crm_err("FAILURE - expected rc %d != %d(%s) for cmd - %s", expected_rc, rc, pcmk_strerror(rc), str); \
168 crm_exit(CRM_EX_ERROR); \
169 } else if (expected_notifications) { \
170 crm_err("FAILURE - expected %d notifications, got only %d for cmd - %s", \
171 num_notifications, num_notifications - expected_notifications, str); \
172 crm_exit(CRM_EX_ERROR); \
173 } else { \
174 if (verbose) { \
175 crm_info("SUCCESS - %s: %d", str, rc); \
176 } else { \
177 crm_debug("SUCCESS - %s: %d", str, rc); \
178 } \
179 } \
180 }\
181
182 static void
183 run_fence_failure_test(void)
184 {
185 stonith_key_value_t *params = NULL;
186
187 params = stonith_key_value_add(params, "pcmk_host_map", "false_1_node1=1,2 false_1_node2=3,4");
188 params = stonith_key_value_add(params, "mode", "fail");
189
190 single_test(st->
191 cmds->register_device(st, st_opts, "test-id1", "stonith-ng", "fence_dummy", params),
192 "Register device1 for failure test", 1, 0);
193
194 single_test(st->cmds->fence(st, st_opts, "false_1_node2", "off", 3, 0),
195 "Fence failure results off", 1, -pcmk_err_generic);
196
197 single_test(st->cmds->fence(st, st_opts, "false_1_node2", "reboot", 3, 0),
198 "Fence failure results reboot", 1, -pcmk_err_generic);
199
200 single_test(st->cmds->remove_device(st, st_opts, "test-id1"),
201 "Remove device1 for failure test", 1, 0);
202
203 stonith_key_value_freeall(params, 1, 1);
204 }
205
206 static void
207 run_fence_failure_rollover_test(void)
208 {
209 stonith_key_value_t *params = NULL;
210
211 params = stonith_key_value_add(params, "pcmk_host_map", "false_1_node1=1,2 false_1_node2=3,4");
212 params = stonith_key_value_add(params, "mode", "fail");
213
214 single_test(st->
215 cmds->register_device(st, st_opts, "test-id1", "stonith-ng", "fence_dummy", params),
216 "Register device1 for rollover test", 1, 0);
217 stonith_key_value_freeall(params, 1, 1);
218 params = NULL;
219 params = stonith_key_value_add(params, "pcmk_host_map", "false_1_node1=1,2 false_1_node2=3,4");
220 params = stonith_key_value_add(params, "mode", "pass");
221
222 single_test(st->
223 cmds->register_device(st, st_opts, "test-id2", "stonith-ng", "fence_dummy", params),
224 "Register device2 for rollover test", 1, 0);
225
226 single_test(st->cmds->fence(st, st_opts, "false_1_node2", "off", 3, 0),
227 "Fence rollover results off", 1, 0);
228
229
230 single_test(st->cmds->fence(st, st_opts, "false_1_node2", "on", 3, 0),
231 "Fence rollover results on", 1, -ENODEV);
232
233 single_test(st->cmds->remove_device(st, st_opts, "test-id1"),
234 "Remove device1 for rollover tests", 1, 0);
235
236 single_test(st->cmds->remove_device(st, st_opts, "test-id2"),
237 "Remove device2 for rollover tests", 1, 0);
238
239 stonith_key_value_freeall(params, 1, 1);
240 }
241
242 static void
243 run_standard_test(void)
244 {
245 stonith_key_value_t *params = NULL;
246
247 params = stonith_key_value_add(params, "pcmk_host_map", "false_1_node1=1,2 false_1_node2=3,4");
248 params = stonith_key_value_add(params, "mode", "pass");
249 params = stonith_key_value_add(params, "mock_dynamic_hosts", "false_1_node1 false_1_node2");
250
251 single_test(st->
252 cmds->register_device(st, st_opts, "test-id", "stonith-ng", "fence_dummy", params),
253 "Register", 1, 0);
254 stonith_key_value_freeall(params, 1, 1);
255 params = NULL;
256
257 single_test(st->cmds->list(st, st_opts, "test-id", NULL, 1), "list", 1, 0);
258
259 single_test(st->cmds->monitor(st, st_opts, "test-id", 1), "Monitor", 1, 0);
260
261 single_test(st->cmds->status(st, st_opts, "test-id", "false_1_node2", 1),
262 "Status false_1_node2", 1, 0);
263
264 single_test(st->cmds->status(st, st_opts, "test-id", "false_1_node1", 1),
265 "Status false_1_node1", 1, 0);
266
267 single_test(st->cmds->fence(st, st_opts, "unknown-host", "off", 1, 0),
268 "Fence unknown-host (expected failure)", 0, -ENODEV);
269
270 single_test(st->cmds->fence(st, st_opts, "false_1_node1", "off", 1, 0),
271 "Fence false_1_node1", 1, 0);
272
273
274 single_test(st->cmds->fence(st, st_opts, "false_1_node1", "on", 1, 0),
275 "Unfence false_1_node1", 1, -ENODEV);
276
277
278 single_test(st->cmds->register_level(st, st_opts, "node1", 999, params),
279 "Attempt to register an invalid level index", 0, -EINVAL);
280
281 single_test(st->cmds->remove_device(st, st_opts, "test-id"), "Remove test-id", 1, 0);
282
283 stonith_key_value_freeall(params, 1, 1);
284 }
285
286 static void
287 sanity_tests(void)
288 {
289 int rc = 0;
290
291 rc = st->cmds->connect(st, crm_system_name, &pollfd.fd);
292 if (rc != pcmk_ok) {
293 stonith_api_delete(st);
294 crm_exit(CRM_EX_DISCONNECT);
295 }
296 st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT, st_callback);
297 st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, st_callback);
298 st->cmds->register_notification(st, STONITH_OP_DEVICE_ADD, st_callback);
299 st->cmds->register_notification(st, STONITH_OP_DEVICE_DEL, st_callback);
300 st->cmds->register_callback(st, 0, 120, st_opt_timeout_updates, NULL, "st_global_callback",
301 st_global_callback);
302
303 crm_info("Starting API Sanity Tests");
304 run_standard_test();
305 run_fence_failure_test();
306 run_fence_failure_rollover_test();
307 crm_info("Sanity Tests Passed");
308 }
309
310 static void
311 standard_dev_test(void)
312 {
313 int rc = 0;
314 char *tmp = NULL;
315 stonith_key_value_t *params = NULL;
316
317 rc = st->cmds->connect(st, crm_system_name, &pollfd.fd);
318 if (rc != pcmk_ok) {
319 stonith_api_delete(st);
320 crm_exit(CRM_EX_DISCONNECT);
321 }
322
323 params = stonith_key_value_add(params, "pcmk_host_map", "some-host=pcmk-7 true_1_node1=3,4");
324
325 rc = st->cmds->register_device(st, st_opts, "test-id", "stonith-ng", "fence_xvm", params);
326 crm_debug("Register: %d", rc);
327
328 rc = st->cmds->list(st, st_opts, "test-id", &tmp, 10);
329 crm_debug("List: %d output: %s", rc, tmp ? tmp : "<none>");
330
331 rc = st->cmds->monitor(st, st_opts, "test-id", 10);
332 crm_debug("Monitor: %d", rc);
333
334 rc = st->cmds->status(st, st_opts, "test-id", "false_1_node2", 10);
335 crm_debug("Status false_1_node2: %d", rc);
336
337 rc = st->cmds->status(st, st_opts, "test-id", "false_1_node1", 10);
338 crm_debug("Status false_1_node1: %d", rc);
339
340 rc = st->cmds->fence(st, st_opts, "unknown-host", "off", 60, 0);
341 crm_debug("Fence unknown-host: %d", rc);
342
343 rc = st->cmds->status(st, st_opts, "test-id", "false_1_node1", 10);
344 crm_debug("Status false_1_node1: %d", rc);
345
346 rc = st->cmds->fence(st, st_opts, "false_1_node1", "off", 60, 0);
347 crm_debug("Fence false_1_node1: %d", rc);
348
349 rc = st->cmds->status(st, st_opts, "test-id", "false_1_node1", 10);
350 crm_debug("Status false_1_node1: %d", rc);
351
352 rc = st->cmds->fence(st, st_opts, "false_1_node1", "on", 10, 0);
353 crm_debug("Unfence false_1_node1: %d", rc);
354
355 rc = st->cmds->status(st, st_opts, "test-id", "false_1_node1", 10);
356 crm_debug("Status false_1_node1: %d", rc);
357
358 rc = st->cmds->fence(st, st_opts, "some-host", "off", 10, 0);
359 crm_debug("Fence alias: %d", rc);
360
361 rc = st->cmds->status(st, st_opts, "test-id", "some-host", 10);
362 crm_debug("Status alias: %d", rc);
363
364 rc = st->cmds->fence(st, st_opts, "false_1_node1", "on", 10, 0);
365 crm_debug("Unfence false_1_node1: %d", rc);
366
367 rc = st->cmds->remove_device(st, st_opts, "test-id");
368 crm_debug("Remove test-id: %d", rc);
369
370 stonith_key_value_freeall(params, 1, 1);
371 }
372
373 static void
374 iterate_mainloop_tests(gboolean event_ready);
375
376 static void
377 mainloop_callback(stonith_t * stonith, stonith_callback_data_t * data)
378 {
379 callback_rc = data->rc;
380 iterate_mainloop_tests(TRUE);
381 }
382
383 static int
384 register_callback_helper(int callid)
385 {
386 return st->cmds->register_callback(st,
387 callid,
388 MAINLOOP_DEFAULT_TIMEOUT,
389 st_opt_timeout_updates, NULL, "callback", mainloop_callback);
390 }
391
392 static void
393 test_async_fence_pass(int check_event)
394 {
395 int rc = 0;
396
397 if (check_event) {
398 if (callback_rc != 0) {
399 mainloop_test_done(FALSE);
400 } else {
401 mainloop_test_done(TRUE);
402 }
403 return;
404 }
405
406 rc = st->cmds->fence(st, 0, "true_1_node1", "off", MAINLOOP_DEFAULT_TIMEOUT, 0);
407 if (rc < 0) {
408 crm_err("fence failed with rc %d", rc);
409 mainloop_test_done(FALSE);
410 }
411 register_callback_helper(rc);
412
413 }
414
415 #define CUSTOM_TIMEOUT_ADDITION 10
416 static void
417 test_async_fence_custom_timeout(int check_event)
418 {
419 int rc = 0;
420 static time_t begin = 0;
421
422 if (check_event) {
423 uint32_t diff = (time(NULL) - begin);
424
425 if (callback_rc != -ETIME) {
426 mainloop_test_done(FALSE);
427 } else if (diff < CUSTOM_TIMEOUT_ADDITION + MAINLOOP_DEFAULT_TIMEOUT) {
428 crm_err
429 ("Custom timeout test failed, callback expiration should be updated to %d, actual timeout was %d",
430 CUSTOM_TIMEOUT_ADDITION + MAINLOOP_DEFAULT_TIMEOUT, diff);
431 mainloop_test_done(FALSE);
432 } else {
433 mainloop_test_done(TRUE);
434 }
435 return;
436 }
437 begin = time(NULL);
438
439 rc = st->cmds->fence(st, 0, "custom_timeout_node1", "off", MAINLOOP_DEFAULT_TIMEOUT, 0);
440 if (rc < 0) {
441 crm_err("fence failed with rc %d", rc);
442 mainloop_test_done(FALSE);
443 }
444 register_callback_helper(rc);
445
446 }
447
448 static void
449 test_async_fence_timeout(int check_event)
450 {
451 int rc = 0;
452
453 if (check_event) {
454 if (callback_rc != -ENODEV) {
455 mainloop_test_done(FALSE);
456 } else {
457 mainloop_test_done(TRUE);
458 }
459 return;
460 }
461
462 rc = st->cmds->fence(st, 0, "false_1_node2", "off", MAINLOOP_DEFAULT_TIMEOUT, 0);
463 if (rc < 0) {
464 crm_err("fence failed with rc %d", rc);
465 mainloop_test_done(FALSE);
466 }
467 register_callback_helper(rc);
468
469 }
470
471 static void
472 test_async_monitor(int check_event)
473 {
474 int rc = 0;
475
476 if (check_event) {
477 if (callback_rc) {
478 mainloop_test_done(FALSE);
479 } else {
480 mainloop_test_done(TRUE);
481 }
482 return;
483 }
484
485 rc = st->cmds->monitor(st, 0, "false_1", MAINLOOP_DEFAULT_TIMEOUT);
486 if (rc < 0) {
487 crm_err("monitor failed with rc %d", rc);
488 mainloop_test_done(FALSE);
489 }
490
491 register_callback_helper(rc);
492
493 }
494
495 static void
496 test_register_async_devices(int check_event)
497 {
498 char buf[16] = { 0, };
499 stonith_key_value_t *params = NULL;
500
501 params = stonith_key_value_add(params, "pcmk_host_map", "false_1_node1=1,2");
502 params = stonith_key_value_add(params, "mode", "fail");
503 st->cmds->register_device(st, st_opts, "false_1", "stonith-ng", "fence_dummy", params);
504 stonith_key_value_freeall(params, 1, 1);
505
506 params = NULL;
507 params = stonith_key_value_add(params, "pcmk_host_map", "true_1_node1=1,2");
508 params = stonith_key_value_add(params, "mode", "pass");
509 st->cmds->register_device(st, st_opts, "true_1", "stonith-ng", "fence_dummy", params);
510 stonith_key_value_freeall(params, 1, 1);
511
512 params = NULL;
513 params = stonith_key_value_add(params, "pcmk_host_map", "custom_timeout_node1=1,2");
514 params = stonith_key_value_add(params, "mode", "fail");
515 params = stonith_key_value_add(params, "delay", "1000");
516 snprintf(buf, sizeof(buf) - 1, "%d", MAINLOOP_DEFAULT_TIMEOUT + CUSTOM_TIMEOUT_ADDITION);
517 params = stonith_key_value_add(params, "pcmk_off_timeout", buf);
518 st->cmds->register_device(st, st_opts, "false_custom_timeout", "stonith-ng", "fence_dummy",
519 params);
520 stonith_key_value_freeall(params, 1, 1);
521
522 mainloop_test_done(TRUE);
523 }
524
525 static void
526 try_mainloop_connect(int check_event)
527 {
528 int rc = stonith_api_connect_retry(st, crm_system_name, 10);
529
530 if (rc == pcmk_ok) {
531 mainloop_test_done(TRUE);
532 return;
533 }
534 crm_err("API CONNECTION FAILURE");
535 mainloop_test_done(FALSE);
536 }
537
538 static void
539 iterate_mainloop_tests(gboolean event_ready)
540 {
541 static mainloop_test_iteration_cb callbacks[] = {
542 try_mainloop_connect,
543 test_register_async_devices,
544 test_async_monitor,
545 test_async_fence_pass,
546 test_async_fence_timeout,
547 test_async_fence_custom_timeout,
548 };
549
550 if (mainloop_iter == (sizeof(callbacks) / sizeof(mainloop_test_iteration_cb))) {
551
552 crm_info("ALL MAINLOOP TESTS PASSED!");
553 crm_exit(CRM_EX_OK);
554 }
555
556 callbacks[mainloop_iter] (event_ready);
557 }
558
559 static gboolean
560 trigger_iterate_mainloop_tests(gpointer user_data)
561 {
562 iterate_mainloop_tests(FALSE);
563 return TRUE;
564 }
565
566 static void
567 test_shutdown(int nsig)
568 {
569 int rc = 0;
570
571 if (st) {
572 rc = st->cmds->disconnect(st);
573 crm_info("Disconnect: %d", rc);
574
575 crm_debug("Destroy");
576 stonith_api_delete(st);
577 }
578
579 if (rc) {
580 crm_exit(CRM_EX_ERROR);
581 }
582 }
583
584 static void
585 mainloop_tests(void)
586 {
587 trig = mainloop_add_trigger(G_PRIORITY_HIGH, trigger_iterate_mainloop_tests, NULL);
588 mainloop_set_trigger(trig);
589 mainloop_add_signal(SIGTERM, test_shutdown);
590
591 crm_info("Starting");
592 mainloop = g_main_loop_new(NULL, FALSE);
593 g_main_loop_run(mainloop);
594 }
595
596 int
597 main(int argc, char **argv)
598 {
599 int argerr = 0;
600 int flag;
601 int option_index = 0;
602
603 enum test_modes mode = test_standard;
604
605 crm_log_cli_init("cts-fence-helper");
606 pcmk__set_cli_options(NULL, "<mode> [options]", long_options,
607 "inject commands into the Pacemaker fencer, "
608 "and watch for events");
609
610 while (1) {
611 flag = pcmk__next_cli_option(argc, argv, &option_index, NULL);
612 if (flag == -1) {
613 break;
614 }
615
616 switch (flag) {
617 case 'V':
618 verbose = 1;
619 break;
620 case '$':
621 case '?':
622 pcmk__cli_help(flag, CRM_EX_OK);
623 break;
624 case 'p':
625 mode = test_passive;
626 break;
627 case 't':
628 mode = test_api_sanity;
629 break;
630 case 'm':
631 mode = test_api_mainloop;
632 break;
633 default:
634 ++argerr;
635 break;
636 }
637 }
638
639 crm_log_init(NULL, LOG_INFO, TRUE, (verbose? TRUE : FALSE), argc, argv,
640 FALSE);
641
642 if (optind > argc) {
643 ++argerr;
644 }
645
646 if (argerr) {
647 pcmk__cli_help('?', CRM_EX_USAGE);
648 }
649
650 st = stonith_api_new();
651 if (st == NULL) {
652 crm_err("Could not connect to fencer: API memory allocation failed");
653 crm_exit(CRM_EX_DISCONNECT);
654 }
655
656 switch (mode) {
657 case test_standard:
658 standard_dev_test();
659 break;
660 case test_passive:
661 passive_test();
662 break;
663 case test_api_sanity:
664 sanity_tests();
665 break;
666 case test_api_mainloop:
667 mainloop_tests();
668 break;
669 }
670
671 test_shutdown(0);
672 return CRM_EX_OK;
673 }