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