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