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