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