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