1 /*
2 Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #ifdef _WIN32
26 #define DEFAULT_PREFIX "c:/atrt"
27 #endif
28
29 #include <NdbAutoPtr.hpp>
30 #include <NdbOut.hpp>
31 #include "atrt.hpp"
32 #include "test_execution_resources.hpp"
33 #include "process_management.hpp"
34
35 #include <util/File.hpp>
36 #include <FileLogHandler.hpp>
37 #include <SysLogHandler.hpp>
38
39 #include <NdbSleep.h>
40 #include "my_alloc.h" // MEM_ROOT
41 #include "my_sys.h" // my_realpath()
42 #include "my_io.h" // FN_REFLEN
43 #include <ndb_version.h>
44 #include <vector>
45 #include <ndb_version.h>
46 #include "template_utils.h"
47 #include "typelib.h"
48
49 #define PATH_SEPARATOR DIR_SEPARATOR
50 #define TESTCASE_RETRIES_THRESHOLD_WARNING 5
51 #define ATRT_VERSION_NUMBER 8
52
53 /** Global variables */
54 static const char progname[] = "ndb_atrt";
55 static const char *g_gather_progname = 0;
56 static const char *g_analyze_progname = 0;
57 static const char *g_setup_progname = 0;
58
59 static const char *g_log_filename = 0;
60 static const char *g_test_case_filename = 0;
61 static const char *g_report_filename = 0;
62
63 static int g_do_setup = 0;
64 static int g_do_deploy = 0;
65 static int g_do_sshx = 0;
66 static int g_do_start = 0;
67 static int g_do_quit = 0;
68
69 static int g_help = 0;
70 static int g_verbosity = 1;
71 static FILE *g_report_file = 0;
72 static FILE *g_test_case_file = stdin;
73 static int g_mode = 0;
74
75 Logger g_logger;
76 atrt_config g_config;
77 const char *g_user = 0;
78 int g_baseport = 10000;
79 int g_fqpn = 0;
80 int g_fix_nodeid = 0;
81 int g_default_ports = 0;
82 int g_mt = 0;
83 int g_mt_rr = 0;
84 int g_restart = 0;
85 int g_default_max_retries = 0;
86 static int g_default_force_cluster_restart = 0;
87 FailureMode g_default_behaviour_on_failure = Restart;
88 const char *default_behaviour_on_failure[] = {"Restart", "Abort", "Skip",
89 "Continue", NullS};
90 TYPELIB behaviour_typelib = {array_elements(default_behaviour_on_failure) - 1,
91 "default_behaviour_on_failure",
92 default_behaviour_on_failure, NULL};
93
94 const char *g_cwd = 0;
95 const char *g_basedir = 0;
96 const char *g_my_cnf = 0;
97 const char *g_prefix = NULL;
98 const char *g_prefix0 = NULL;
99 const char *g_prefix1 = NULL;
100 const char *g_clusters = 0;
101 const char *g_config_type = NULL; // "cnf" or "ini"
102 const char *g_site = NULL;
103 BaseString g_replicate;
104 const char *save_file = 0;
105 const char *save_group_suffix = 0;
106 const char *g_dummy;
107 char *g_env_path = 0;
108 const char *g_mysqld_host = 0;
109
110 TestExecutionResources g_resources;
111
112 static BaseString get_atrt_path(const char *arg);
113
114 const char *g_search_path[] = {"bin", "libexec", "sbin", "scripts",
115 "lib", "lib/mysql", 0};
116 static bool find_scripts(const char *path);
117 static bool find_config_ini_files();
118
119 TestResult run_test_case(ProcessManagement& processManagement,
120 const atrt_testcase& testcase,
121 bool is_last_testcase,
122 bool next_testcase_forces_restart);
123 int test_case_init(ProcessManagement &, const atrt_testcase &);
124 int test_case_execution_loop(ProcessManagement &, const time_t, const time_t);
125 void test_case_results(TestResult *, const atrt_testcase &);
126
127 bool do_command(ProcessManagement& processManagement,
128 atrt_config& config);
129 bool setup_test_case(ProcessManagement& processManagement,
130 atrt_config&, const atrt_testcase&);
131 /**
132 * check configuration if any changes has been
133 * done for the duration of the latest running test
134 * if so, return true, and reset those changes
135 * (true, indicates that a restart is needed to actually
136 * reset the running processes)
137 */
138 bool reset_config(ProcessManagement &processManagement, atrt_config&);
139
140 static struct my_option g_options[] = {
141 {"help", '?', "Display this help and exit.", (uchar **)&g_help,
142 (uchar **)&g_help, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
143 {"version", 'V', "Output version information and exit.", 0, 0, 0,
144 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
145 {"site", 256, "Site", (uchar **)&g_site, (uchar **)&g_site, 0, GET_STR,
146 REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
147 {"clusters", 256, "Cluster", (uchar **)&g_clusters, (uchar **)&g_clusters,
148 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
149 {"config-type", 256, "cnf (default) or ini", (uchar **)&g_config_type,
150 (uchar **)&g_config_type, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
151 {"mysqld", 256, "atrt mysqld", (uchar **)&g_mysqld_host,
152 (uchar **)&g_mysqld_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
153 {"replicate", 1024, "replicate", (uchar **)&g_dummy, (uchar **)&g_dummy, 0,
154 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
155 {"log-file", 256, "log-file", (uchar **)&g_log_filename,
156 (uchar **)&g_log_filename, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
157 {"testcase-file", 'f', "testcase-file", (uchar **)&g_test_case_filename,
158 (uchar **)&g_test_case_filename, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0,
159 0},
160 {"report-file", 'r', "report-file", (uchar **)&g_report_filename,
161 (uchar **)&g_report_filename, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
162 {"basedir", 256, "Base path", (uchar **)&g_basedir, (uchar **)&g_basedir, 0,
163 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
164 {"baseport", 256, "Base port", (uchar **)&g_baseport, (uchar **)&g_baseport,
165 0, GET_INT, REQUIRED_ARG, g_baseport, 0, 0, 0, 0, 0},
166 {"prefix", 256, "atrt install dir", (uchar **)&g_prefix,
167 (uchar **)&g_prefix, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
168 {"prefix0", 256, "mysql install dir", (uchar **)&g_prefix0,
169 (uchar **)&g_prefix0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
170 {"prefix1", 256, "mysql install dir 1", (uchar **)&g_prefix1,
171 (uchar **)&g_prefix1, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
172 {"verbose", 'v', "Verbosity", (uchar **)&g_verbosity,
173 (uchar **)&g_verbosity, 0, GET_INT, REQUIRED_ARG, g_verbosity, 0, 0, 0, 0,
174 0},
175 {"configure", 256, "configure", (uchar **)&g_do_setup,
176 (uchar **)&g_do_setup, 0, GET_INT, REQUIRED_ARG, g_do_setup, 0, 0, 0, 0,
177 0},
178 {"deploy", 256, "deploy", (uchar **)&g_do_deploy, (uchar **)&g_do_deploy, 0,
179 GET_INT, REQUIRED_ARG, g_do_deploy, 0, 0, 0, 0, 0},
180 {"sshx", 256, "sshx", (uchar **)&g_do_sshx, (uchar **)&g_do_sshx, 0,
181 GET_INT, REQUIRED_ARG, g_do_sshx, 0, 0, 0, 0, 0},
182 {"start", 256, "start", (uchar **)&g_do_start, (uchar **)&g_do_start, 0,
183 GET_INT, REQUIRED_ARG, g_do_start, 0, 0, 0, 0, 0},
184 {"fqpn", 256, "Fully qualified path-names ", (uchar **)&g_fqpn,
185 (uchar **)&g_fqpn, 0, GET_INT, REQUIRED_ARG, g_fqpn, 0, 0, 0, 0, 0},
186 {"fix-nodeid", 256, "Fix nodeid for each started process ",
187 (uchar **)&g_fix_nodeid, (uchar **)&g_fix_nodeid, 0, GET_INT, REQUIRED_ARG,
188 g_fqpn, 0, 0, 0, 0, 0},
189 {"default-ports", 256, "Use default ports when possible",
190 (uchar **)&g_default_ports, (uchar **)&g_default_ports, 0, GET_INT,
191 REQUIRED_ARG, g_default_ports, 0, 0, 0, 0, 0},
192 {"mode", 256, "Mode 0=interactive 1=regression 2=bench", (uchar **)&g_mode,
193 (uchar **)&g_mode, 0, GET_INT, REQUIRED_ARG, g_mode, 0, 0, 0, 0, 0},
194 {"quit", 256, "Quit before starting tests", (uchar **)&g_do_quit,
195 (uchar **)&g_do_quit, 0, GET_BOOL, NO_ARG, g_do_quit, 0, 0, 0, 0, 0},
196 {"mt", 256, "Use ndbmtd (0 = never, 1 = round-robin, 2 = only)",
197 (uchar **)&g_mt, (uchar **)&g_mt, 0, GET_INT, REQUIRED_ARG, g_mt, 0, 0, 0,
198 0, 0},
199 {"default-max-retries", 256,
200 "default number of retries after a test case fails (can be overwritten in "
201 "the test suite file)",
202 (uchar **)&g_default_max_retries, (uchar **)&g_default_max_retries, 0,
203 GET_INT, REQUIRED_ARG, g_default_max_retries, 0, 0, 0, 0, 0},
204 {"default-force-cluster-restart", 0,
205 "Force cluster to restart for each testrun (can be overwritten in test "
206 "suite file)",
207 (uchar **)&g_default_force_cluster_restart,
208 (uchar **)&g_default_force_cluster_restart, 0, GET_BOOL, NO_ARG,
209 g_default_force_cluster_restart, 0, 0, 0, 0, 0},
210 {"default-behaviour-on-failure", 256, "default to do when a test fails",
211 (uchar **)&g_default_behaviour_on_failure,
212 (uchar **)&g_default_behaviour_on_failure, &behaviour_typelib, GET_ENUM,
213 REQUIRED_ARG, g_default_behaviour_on_failure, 0, 0, 0, 0, 0},
214 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}};
215
216 static int check_testcase_file_main(int argc, char **argv);
217 static void print_testcase_file_syntax();
218
main(int argc,char ** argv)219 int main(int argc, char **argv) {
220 ndb_init();
221
222 AtrtExitCodes return_code = AtrtExitCodes::TESTSUITE_SUCCESS;
223
224 g_logger.setCategory(progname);
225 g_logger.enable(Logger::LL_ALL);
226 g_logger.createConsoleHandler();
227
228 // If program is called with --check-testcase-files as first option
229 // it is assumed that the rest of command line arguments are
230 // testcase-filenames and those files will be syntax checked.
231 if (argc >= 2 && strcmp(argv[1], "--check-testcase-files") == 0) {
232 exit(check_testcase_file_main(argc, argv));
233 }
234
235 MEM_ROOT alloc = MEM_ROOT{PSI_NOT_INSTRUMENTED, 512};
236 if (!parse_args(argc, argv, &alloc)) {
237 g_logger.critical("Failed to parse arguments");
238 return atrt_exit(ATRT_FAILURE);
239 }
240
241 g_logger.info("Starting ATRT version : %s", getAtrtVersion().c_str());
242
243 if (g_mt != 0) {
244 g_resources.setRequired(g_resources.NDBMTD);
245 }
246
247 {
248 std::vector<std::string> error;
249 std::vector<std::string> info;
250 if (!g_resources.loadPaths(g_prefix0, g_prefix1, &error, &info)) {
251 g_logger.critical("Failed to find required binaries for execution");
252
253 for (auto msg : error) {
254 g_logger.critical("%s", msg.c_str());
255 }
256 return atrt_exit(ATRT_FAILURE);
257 }
258
259 for (auto msg : info) {
260 g_logger.info("%s", msg.c_str());
261 }
262 }
263
264 {
265 BaseString atrt_path = get_atrt_path(argv[0]);
266 assert(atrt_path != "");
267
268 if (!find_scripts(atrt_path.c_str())) {
269 g_logger.critical("Failed to find required atrt scripts for execution");
270 return atrt_exit(ATRT_FAILURE);
271 }
272 }
273
274 g_config.m_config_type = atrt_config::CNF;
275 if (g_config_type != NULL && strcmp(g_config_type, "ini") == 0) {
276 g_logger.info("Using config.ini for cluster configuration");
277 g_config.m_config_type = atrt_config::INI;
278
279 if (!find_config_ini_files()) {
280 g_logger.critical("Failed to find required config.ini files");
281 return atrt_exit(ATRT_FAILURE);
282 }
283 }
284
285 g_config.m_generated = false;
286 g_config.m_replication = g_replicate;
287 if (!setup_config(g_config, g_mysqld_host)) {
288 g_logger.critical("Failed to setup configuration");
289 return atrt_exit(ATRT_FAILURE);
290 }
291
292 if (!g_config.m_processes.size()) {
293 g_logger.critical("Error: No processes defined in cluster configuration");
294 return atrt_exit(ATRT_FAILURE);
295 }
296
297 if (!configure(g_config, g_do_setup)) {
298 g_logger.critical("Failed to configure");
299 return atrt_exit(ATRT_FAILURE);
300 }
301
302 g_logger.info("Setting up directories...");
303 if (!setup_directories(g_config, g_do_setup)) {
304 g_logger.critical("Failed to set up directories");
305 return atrt_exit(ATRT_FAILURE);
306 }
307
308 if (g_do_setup) {
309 g_logger.info("Setting up files...");
310 if (!setup_files(g_config, g_do_setup, g_do_sshx)) {
311 g_logger.critical("Failed to set up files");
312 return atrt_exit(ATRT_FAILURE);
313 }
314 }
315
316 if (g_do_deploy) {
317 g_logger.info("Deploying files...");
318 if (!deploy(g_do_deploy, g_config)) {
319 g_logger.critical("Failed to deploy");
320 return atrt_exit(ATRT_FAILURE);
321 }
322 }
323
324 if (g_do_quit) {
325 return atrt_exit(TESTSUITE_SUCCESS);
326 }
327
328 if (!setup_hosts(g_config)) {
329 g_logger.critical("Failed to setup hosts");
330 return atrt_exit(ATRT_FAILURE);
331 }
332
333 ProcessManagement processManagement(g_config, g_setup_progname);
334
335 if (g_do_sshx) {
336 g_logger.info("Starting xterm-ssh");
337 if (!sshx(g_config, g_do_sshx)) {
338 g_logger.critical("Failed to start xterm-ssh");
339 return atrt_exit(ATRT_FAILURE);
340 }
341
342 g_logger.info("Done...sleeping");
343 while (true) {
344 if (!do_command(processManagement, g_config)) {
345 g_logger.critical("Failed to do ssh command");
346 return atrt_exit(ATRT_FAILURE);
347 }
348
349 NdbSleep_SecSleep(1);
350 }
351 return atrt_exit(TESTSUITE_SUCCESS);
352 }
353
354 /**
355 * contact each ndb_cpcd
356 */
357 g_logger.info("Connecting to hosts...");
358 if (!connect_hosts(g_config)) {
359 g_logger.critical("Failed to connect to CPCD on hosts");
360 return atrt_exit(ATRT_FAILURE);
361 }
362
363 /**
364 * Collect all the testcases
365 */
366 std::vector<atrt_testcase> testcases;
367 if (!read_test_cases(g_test_case_file, &testcases)) {
368 g_logger.critical("Failed to read all the testcases");
369 return atrt_exit(ATRT_FAILURE);
370 }
371
372 /**
373 * Run all tests
374 */
375
376 g_logger.debug("Entering main loop");
377 FailureMode current_failure_mode = FailureMode::Continue;
378 unsigned int last_testcase_idx = testcases.size() - 1;
379 for (unsigned int i = 0; i <= last_testcase_idx; i++) {
380 atrt_testcase testcase = testcases[i];
381 g_logger.info("#%d - %s", testcase.test_no, testcase.m_name.c_str());
382
383 TestResult test_result;
384 if (current_failure_mode == FailureMode::Skip) {
385 test_result = {0, 0, ERR_TEST_SKIPPED};
386 } else {
387 bool is_last_testcase = last_testcase_idx == i;
388 bool next_testcase_forces_restart = false;
389 if (!is_last_testcase) {
390 next_testcase_forces_restart = testcases[i+1].m_force_cluster_restart;
391 }
392 test_result = run_test_case(processManagement, testcase,
393 is_last_testcase,
394 next_testcase_forces_restart);
395 if (test_result.result != ErrorCodes::ERR_OK) {
396 current_failure_mode = testcase.m_behaviour_on_failure;
397 }
398 }
399 update_atrt_result_code(test_result, &return_code);
400
401 if (g_report_file != 0) {
402 fprintf(g_report_file, "%s ; %d ; %d ; %ld ; %d\n",
403 testcase.m_name.c_str(), testcase.test_no, test_result.result,
404 test_result.elapsed, test_result.testruns);
405 fflush(g_report_file);
406 }
407
408 if (g_mode == 0 && test_result.result != ERR_OK) {
409 g_logger.info("Encountered failed test in interactive mode");
410 }
411
412 const char *test_status = get_test_status(test_result.result);
413 g_logger.info("#%d %s(%d)", testcase.test_no, test_status,
414 test_result.result);
415
416 if (current_failure_mode == FailureMode::Abort) {
417 g_logger.info("Aborting the test suite execution!");
418 break;
419 }
420 }
421
422 if (g_report_file != 0) {
423 fclose(g_report_file);
424 g_report_file = 0;
425 }
426
427 g_logger.info("Finishing, result: %d", return_code);
428 return return_code;
429 }
430
get_one_option(int arg,const struct my_option * opt,char * value)431 extern "C" bool get_one_option(int arg, const struct my_option *opt,
432 char *value) {
433 if (arg == 1024) {
434 if (g_replicate.length()) g_replicate.append(";");
435 g_replicate.append(value);
436 return 0;
437 }
438 return 0;
439 }
440
parse_args(int argc,char ** argv,MEM_ROOT * alloc)441 bool parse_args(int argc, char **argv, MEM_ROOT *alloc) {
442 bool fail_after_help = false;
443 char buf[2048];
444
445 if (argc >= 2 &&
446 (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)) {
447 ndbout << getAtrtVersion().c_str() << endl;
448 exit(0);
449 }
450
451 if (getcwd(buf, sizeof(buf)) == 0) {
452 g_logger.error("Unable to get current working directory");
453 return false;
454 }
455 g_cwd = strdup(buf);
456
457 struct stat sbuf;
458 BaseString mycnf;
459 mycnf.append(g_cwd);
460 mycnf.append(DIR_SEPARATOR);
461
462 if (argc > 1 && lstat(argv[argc - 1], &sbuf) == 0) {
463 mycnf.append(argv[argc - 1]);
464 } else {
465 mycnf.append("my.cnf");
466 if (lstat(mycnf.c_str(), &sbuf) != 0) {
467 g_logger.error(
468 "Could not find out which config file to use! "
469 "Pass it as last argument to atrt: 'atrt <config file>' "
470 "(default: '%s')",
471 mycnf.c_str());
472 fail_after_help = true;
473 }
474 }
475
476 to_fwd_slashes((char *)g_cwd);
477
478 g_logger.info("Bootstrapping using %s", mycnf.c_str());
479
480 const char *groups[] = {"atrt", 0};
481 int ret = load_defaults(mycnf.c_str(), groups, &argc, &argv, alloc);
482
483 if (ret) {
484 g_logger.error("Failed to load defaults, returned (%d)", ret);
485 return false;
486 }
487
488 save_file = my_defaults_file;
489 save_group_suffix = my_defaults_group_suffix;
490
491 if (my_defaults_extra_file) {
492 g_logger.error("--defaults-extra-file(%s) is not supported...",
493 my_defaults_extra_file);
494 return false;
495 }
496
497 ret = handle_options(&argc, &argv, g_options, get_one_option);
498 if (ret) {
499 g_logger.error("handle_options failed, ret: %d, argc: %d, *argv: '%s'", ret,
500 argc, *argv);
501 return false;
502 }
503
504 if (argc >= 2) {
505 const char *arg = argv[argc - 2];
506 while (*arg) {
507 switch (*arg) {
508 case 'c':
509 g_do_setup = (g_do_setup == 0) ? 1 : g_do_setup;
510 break;
511 case 'C':
512 g_do_setup = 2;
513 break;
514 case 'd':
515 g_do_deploy = 3;
516 break;
517 case 'D':
518 g_do_deploy = 2; // only binaries
519 break;
520 case 'x':
521 g_do_sshx = atrt_process::AP_CLIENT | atrt_process::AP_NDB_API;
522 break;
523 case 'X':
524 g_do_sshx = atrt_process::AP_ALL;
525 break;
526 case 's':
527 g_do_start = ProcessManagement::P_NDB;
528 break;
529 case 'S':
530 g_do_start = ProcessManagement::P_NDB | ProcessManagement::P_SERVERS;
531 break;
532 case 'f':
533 g_fqpn = 1;
534 break;
535 case 'z':
536 g_fix_nodeid = 1;
537 break;
538 case 'q':
539 g_do_quit = 1;
540 break;
541 case 'r':
542 g_restart = 1;
543 break;
544 default:
545 g_logger.error("Unknown switch '%c'", *arg);
546 return false;
547 }
548 arg++;
549 }
550 }
551
552 if (g_log_filename != 0) {
553 g_logger.removeConsoleHandler();
554 g_logger.addHandler(new FileLogHandler(g_log_filename));
555 }
556
557 {
558 int tmp = Logger::LL_WARNING - g_verbosity;
559 tmp = (tmp < Logger::LL_DEBUG ? Logger::LL_DEBUG : tmp);
560 g_logger.disable(Logger::LL_ALL);
561 g_logger.enable(Logger::LL_ON);
562 g_logger.enable((Logger::LoggerLevel)tmp, Logger::LL_ALERT);
563 }
564
565 if (!g_basedir) {
566 g_basedir = g_cwd;
567 g_logger.info("basedir not specified, using %s", g_basedir);
568 } else {
569 g_logger.info("basedir, %s", g_basedir);
570 }
571
572 const char *default_prefix;
573 if (g_prefix != NULL) {
574 default_prefix = g_prefix;
575 } else if (g_prefix0 != NULL) {
576 default_prefix = g_prefix0;
577 } else {
578 default_prefix = DEFAULT_PREFIX;
579 }
580
581 if (g_prefix == NULL) {
582 g_prefix = DEFAULT_PREFIX;
583 }
584
585 if (g_prefix0 == NULL) {
586 g_prefix0 = DEFAULT_PREFIX;
587 }
588
589 /**
590 * Add path to atrt-*.sh
591 */
592 {
593 BaseString tmp;
594 const char *env = getenv("PATH");
595 if (env && strlen(env)) {
596 tmp.assfmt("PATH=%s:%s/mysql-test/ndb", env, g_prefix);
597 } else {
598 tmp.assfmt("PATH=%s/mysql-test/ndb", g_prefix);
599 }
600 to_native(tmp);
601 g_env_path = strdup(tmp.c_str());
602 putenv(g_env_path);
603 }
604
605 if (g_help) {
606 my_print_help(g_options);
607 my_print_variables(g_options);
608 print_testcase_file_syntax();
609 return 0;
610 }
611 if (fail_after_help) {
612 return false;
613 }
614
615 if (g_test_case_filename) {
616 g_test_case_file = fopen(g_test_case_filename, "r");
617 if (g_test_case_file == 0) {
618 g_logger.critical("Unable to open file: %s", g_test_case_filename);
619 return false;
620 }
621 if (g_do_setup == 0) g_do_setup = 2;
622
623 if (g_do_start == 0) g_do_start =
624 ProcessManagement::P_NDB | ProcessManagement::P_SERVERS;
625
626 if (g_mode == 0) g_mode = 1;
627
628 if (g_do_sshx) {
629 g_logger.critical("ssx specified...not possible with testfile");
630 return false;
631 }
632 } else {
633 g_logger.info(
634 "No test case file given with -f <test file>, "
635 "running in interactive mode from stdin");
636 }
637
638 if (g_do_setup == 0) {
639 BaseString tmp;
640 tmp.append(g_basedir);
641 tmp.append(PATH_SEPARATOR);
642 tmp.append("my.cnf");
643 if (lstat(tmp.c_str(), &sbuf) != 0) {
644 g_logger.error(
645 "Could not find a my.cnf file in the basedir '%s', "
646 "you probably need to configure it with "
647 "'atrt --configure=1 <config_file>'",
648 g_basedir);
649 return false;
650 }
651
652 if (!S_ISREG(sbuf.st_mode)) {
653 g_logger.error("%s is not a regular file", tmp.c_str());
654 return false;
655 }
656
657 g_my_cnf = strdup(tmp.c_str());
658 g_logger.info("Using %s", tmp.c_str());
659 } else {
660 g_my_cnf = strdup(mycnf.c_str());
661 }
662
663 if (g_prefix1) {
664 g_logger.info("Using --prefix1=\"%s\"", g_prefix1);
665 }
666
667 if (g_report_filename) {
668 g_report_file = fopen(g_report_filename, "w");
669 if (g_report_file == 0) {
670 g_logger.critical("Unable to create report file: %s", g_report_filename);
671 return false;
672 }
673 }
674
675 if (g_clusters == 0) {
676 g_logger.critical("No clusters specified");
677 return false;
678 }
679
680 /* Read username from environment, default to sakila */
681 const char * logname = getenv("LOGNAME");
682 if ((logname != nullptr) && (strlen(logname) > 0)) {
683 g_user = strdup(logname);
684 } else {
685 g_user = "sakila";
686 g_logger.info(
687 "No default user specified, will use 'sakila'. "
688 "Please set LOGNAME environment variable for other username");
689 }
690
691 return true;
692 }
693
getAtrtVersion()694 std::string getAtrtVersion() {
695 int mysql_version = ndbGetOwnVersion();
696 std::string version = std::to_string(ndbGetMajor(mysql_version)) + "." +
697 std::to_string(ndbGetMinor(mysql_version)) + "." +
698 std::to_string(ndbGetBuild(mysql_version)) + "." +
699 std::to_string(ATRT_VERSION_NUMBER);
700 return version;
701 }
702
connect_hosts(atrt_config & config)703 bool connect_hosts(atrt_config &config) {
704 for (unsigned i = 0; i < config.m_hosts.size(); i++) {
705 if (config.m_hosts[i]->m_hostname.length() == 0) continue;
706
707 if (config.m_hosts[i]->m_cpcd->connect() != 0) {
708 g_logger.error("Unable to connect to cpc %s:%d",
709 config.m_hosts[i]->m_cpcd->getHost(),
710 config.m_hosts[i]->m_cpcd->getPort());
711 return false;
712 }
713 g_logger.debug("Connected to %s:%d", config.m_hosts[i]->m_cpcd->getHost(),
714 config.m_hosts[i]->m_cpcd->getPort());
715 }
716
717 return true;
718 }
719
is_client_running(atrt_config & config)720 bool is_client_running(atrt_config &config) {
721 for (unsigned i = 0; i < config.m_processes.size(); i++) {
722 atrt_process &proc = *config.m_processes[i];
723 if ((ProcessManagement::P_CLIENTS & proc.m_type) != 0
724 && proc.m_proc.m_status == "running") {
725 return true;
726 }
727 }
728 return false;
729 }
730
get_test_status(int result)731 const char *get_test_status(int result) {
732 switch (result) {
733 case ErrorCodes::ERR_OK:
734 return "OK";
735 case ErrorCodes::ERR_TEST_SKIPPED:
736 return "SKIPPED";
737 case ErrorCodes::ERR_CRITICAL:
738 return "CRITICAL";
739 }
740 return "FAILED";
741 }
742
atrt_exit(int return_code)743 int atrt_exit(int return_code) {
744 g_logger.info("Finishing, result: %d", return_code);
745 return return_code;
746 }
747
read_test_cases(FILE * file,std::vector<atrt_testcase> * testcases)748 bool read_test_cases(FILE *file, std::vector<atrt_testcase> *testcases) {
749 int lineno = 1;
750 int test_no = 1;
751 while (!feof(file)) {
752 atrt_testcase testcase;
753 const int num_element_lines = read_test_case(file, lineno, testcase);
754 if (num_element_lines == 0) {
755 continue;
756 }
757 if (num_element_lines == ERR_CORRUPT_TESTCASE) {
758 g_logger.critical("Corrupted testcase at line %d (error %d)", lineno,
759 num_element_lines);
760 return false;
761 }
762 testcase.test_no = test_no++;
763 testcases->push_back(std::move(testcase));
764 }
765
766 if (file != stdin) {
767 fclose(file);
768 }
769
770 return true;
771 }
772
run_test_case(ProcessManagement & processManagement,const atrt_testcase & testcase,bool is_last_testcase,bool next_testcase_forces_restart)773 TestResult run_test_case(ProcessManagement &processManagement,
774 const atrt_testcase &testcase,
775 bool is_last_testcase,
776 bool next_testcase_forces_restart) {
777 TestResult test_result = {0, 0, 0};
778 for (; test_result.testruns <= testcase.m_max_retries;
779 test_result.testruns++) {
780 if (test_result.testruns > 0) {
781 if (test_result.result == ERR_OK ||
782 test_result.result == ERR_TEST_SKIPPED) {
783 break;
784 }
785 g_logger.info("Retrying #%d - %s (%d/%d)...", testcase.test_no,
786 testcase.m_name.c_str(), test_result.testruns,
787 testcase.m_max_retries);
788 }
789
790 test_result.result = test_case_init(processManagement, testcase);
791
792 if (test_result.result == ERR_OK) {
793 const time_t start = time(0);
794 test_result.result = test_case_execution_loop(processManagement, start,
795 testcase.m_max_time);
796 test_result.elapsed = time(0) - start;
797 }
798
799 if (!processManagement.stopClientProcesses()) {
800 g_logger.critical("Failed to stop client processes");
801 test_result.result = ERR_CRITICAL;
802 }
803
804 test_case_results(&test_result, testcase);
805
806 bool configuration_reset = reset_config(processManagement, g_config);
807 bool restart_on_error = test_result.result != ERR_TEST_SKIPPED &&
808 test_result.result != ERR_OK &&
809 testcase.m_behaviour_on_failure == FailureMode::Restart;
810 bool stop_cluster = is_last_testcase ||
811 next_testcase_forces_restart ||
812 configuration_reset ||
813 restart_on_error;
814 if (stop_cluster) {
815 g_logger.debug("Stopping all cluster processes on condition(s):");
816 if (is_last_testcase) g_logger.debug("- Last test case");
817 if (next_testcase_forces_restart)
818 g_logger.debug("- Next test case forces restart");
819 if (configuration_reset) g_logger.debug("- Configuration forces reset");
820 if (restart_on_error) g_logger.debug("- Restart on test error");
821
822 if (!processManagement.stopAllProcesses()) {
823 g_logger.critical("Failed to stop all processes");
824 test_result.result = ERR_CRITICAL;
825 }
826 }
827 }
828
829 return test_result;
830 }
831
test_case_init(ProcessManagement & processManagement,const atrt_testcase & testcase)832 int test_case_init(ProcessManagement &processManagement,
833 const atrt_testcase &testcase) {
834 g_logger.debug("Starting test case initialization");
835
836 if (!processManagement.startAllProcesses()) {
837 g_logger.critical("Cluster could not be started");
838 return ERR_CRITICAL;
839 }
840
841 g_logger.info("All servers are running and ready");
842
843 // Assign processes to programs
844 if (!setup_test_case(processManagement, g_config, testcase)) {
845 g_logger.critical("Failed to setup test case");
846 return ERR_CRITICAL;
847 }
848
849 if (!processManagement.startClientProcesses()) {
850 g_logger.critical("Failed to start client processes");
851 return ERR_CRITICAL;
852 }
853
854 g_logger.debug("Successful test case initialization");
855
856 return ERR_OK;
857 }
858
test_case_execution_loop(ProcessManagement & processManagement,const time_t start_time,const time_t max_execution_time)859 int test_case_execution_loop(ProcessManagement &processManagement,
860 const time_t start_time,
861 const time_t max_execution_time) {
862 g_logger.debug("Starting test case execution loop");
863
864 const time_t stop_time = start_time + max_execution_time;
865 int result = ERR_OK;
866
867 do {
868 result = processManagement.updateProcessesStatus();
869 if (result != ERR_OK) {
870 g_logger.critical("Failed to get updated status for all processes");
871 return result;
872 }
873
874 if (!is_client_running(g_config)) {
875 g_logger.debug("Finished test case execution loop");
876 return result;
877 }
878
879 if (!do_command(processManagement, g_config)) {
880 g_logger.critical("Failure on client command execution");
881 return ERR_COMMAND_FAILED;
882 }
883
884 time_t now = time(0);
885 if (now > stop_time) {
886 g_logger.info("Timeout after %ld seconds", max_execution_time);
887 return ERR_MAX_TIME_ELAPSED;
888 }
889 NdbSleep_SecSleep(1);
890 } while (true);
891 }
892
test_case_results(TestResult * test_result,const atrt_testcase & testcase)893 void test_case_results(TestResult *test_result, const atrt_testcase &testcase) {
894 int tmp, *rp = test_result->result != ERR_OK ? &tmp : &(test_result->result);
895 g_logger.debug("Starting result gathering");
896
897 if (!gather_result(g_config, rp)) {
898 g_logger.critical("Failed to gather result after test run");
899 test_result->result = ERR_CRITICAL;
900 }
901
902 BaseString res_dir;
903 res_dir.assfmt("result.%d", testcase.test_no);
904 remove_dir(res_dir.c_str(), true);
905
906 if (testcase.m_report || test_result->result != ERR_OK) {
907 if (rename("result", res_dir.c_str()) != 0) {
908 g_logger.critical("Failed to rename %s as %s", "result", res_dir.c_str());
909 remove_dir("result", true);
910 test_result->result = ERR_CRITICAL;
911 }
912 } else {
913 remove_dir("result", true);
914 }
915
916 g_logger.debug("Finished result gathering");
917 }
918
update_atrt_result_code(const TestResult & test_result,AtrtExitCodes * return_code)919 void update_atrt_result_code(const TestResult &test_result,
920 AtrtExitCodes *return_code) {
921 if (*return_code == ATRT_FAILURE) return;
922
923 switch (test_result.result) {
924 case ErrorCodes::ERR_OK:
925 break;
926 case ErrorCodes::ERR_CRITICAL:
927 *return_code = ATRT_FAILURE;
928 break;
929 default:
930 *return_code = TESTSUITE_FAILURES;
931 break;
932 }
933 }
934
insert(const char * pair,Properties & p)935 int insert(const char *pair, Properties &p) {
936 BaseString tmp(pair);
937
938 Vector<BaseString> split;
939 tmp.split(split, ":=", 2);
940
941 if (split.size() != 2) return -1;
942
943 p.put(split[0].trim().c_str(), split[1].trim().c_str());
944
945 return 0;
946 }
947
948 /*
949 * read_test_case - extract one testcase from file
950 *
951 * On success return a positive number with actual lines describing
952 * the test case not counting blank lines and comments.
953 * On end of file it returns 0.
954 * On failure a nehative number is returned.
955 */
read_test_case(FILE * file,int & line,atrt_testcase & tc)956 int read_test_case(FILE *file, int &line, atrt_testcase &tc) {
957 Properties p;
958 int elements = 0;
959 char buf[1024];
960
961 while (!feof(file)) {
962 if (file == stdin) printf("atrt> ");
963 if (!fgets(buf, 1024, file)) break;
964
965 line++;
966 BaseString tmp = buf;
967
968 if (tmp.length() > 0 && tmp.c_str()[0] == '#') continue;
969
970 tmp.trim(" \t\n\r");
971
972 if (tmp.length() == 0) {
973 break; // End of test case definition
974 }
975
976 if (insert(tmp.c_str(), p) != 0) {
977 // Element line had no : or =
978 if (elements == 0 && file == stdin) {
979 // Assume a single line command with command and arguments
980 // separated with a space
981 Vector<BaseString> split;
982 tmp.split(split, " ", 2);
983 tc.m_cmd.m_exe = split[0];
984 if (split.size() == 2)
985 tc.m_cmd.m_args = split[1];
986 else
987 tc.m_cmd.m_args = "";
988 tc.m_max_time = 60000;
989 return 1;
990 }
991 g_logger.critical("Invalid test file: Corrupt line: %d: %s", line, buf);
992 return ERR_CORRUPT_TESTCASE;
993 }
994
995 elements++;
996 }
997
998 if (elements == 0) {
999 // End of file
1000 return 0;
1001 }
1002
1003 int used_elements = 0;
1004
1005 if (!p.get("cmd", tc.m_cmd.m_exe)) {
1006 g_logger.critical(
1007 "Invalid test file: cmd is missing in test case above line: %d", line);
1008 return ERR_CORRUPT_TESTCASE;
1009 }
1010 used_elements++;
1011
1012 if (!p.get("args", tc.m_cmd.m_args))
1013 tc.m_cmd.m_args = "";
1014 else
1015 used_elements++;
1016
1017 const char *mt = 0;
1018 if (!p.get("max-time", &mt))
1019 tc.m_max_time = 60000;
1020 else {
1021 tc.m_max_time = atoi(mt);
1022 used_elements++;
1023 }
1024
1025 if (p.get("type", &mt)) {
1026 tc.m_report = (strcmp(mt, "bench") == 0);
1027 used_elements++;
1028 } else
1029 tc.m_report = false;
1030
1031 if (p.get("run-all", &mt)) {
1032 tc.m_run_all = (strcmp(mt, "yes") == 0);
1033 used_elements++;
1034 } else
1035 tc.m_run_all = false;
1036
1037 const char *str;
1038 if (p.get("mysqld", &str)) {
1039 tc.m_mysqld_options.assign(str);
1040 used_elements++;
1041 } else {
1042 tc.m_mysqld_options.assign("");
1043 }
1044
1045 tc.m_cmd.m_cmd_type = atrt_process::AP_NDB_API;
1046 if (p.get("cmd-type", &str)) {
1047 if (strcmp(str, "mysql") == 0)
1048 tc.m_cmd.m_cmd_type = atrt_process::AP_CLIENT;
1049 used_elements++;
1050 }
1051
1052 if (!p.get("name", &mt)) {
1053 tc.m_name.assfmt("%s %s", tc.m_cmd.m_exe.c_str(), tc.m_cmd.m_args.c_str());
1054 } else {
1055 tc.m_name.assign(mt);
1056 used_elements++;
1057 }
1058
1059 tc.m_force_cluster_restart = g_default_force_cluster_restart;
1060 if (p.get("force-cluster-restart", &str)) {
1061 tc.m_force_cluster_restart = (strcmp(str, "yes") == 0);
1062 used_elements++;
1063 }
1064
1065 tc.m_max_retries = g_default_max_retries;
1066 if (p.get("max-retries", &mt)) {
1067 tc.m_max_retries = atoi(mt);
1068 used_elements++;
1069 }
1070
1071 if (tc.m_max_retries < 0) {
1072 g_logger.error("No of retries must not be less than zero for test '%s'",
1073 tc.m_name.c_str());
1074 return ERR_CORRUPT_TESTCASE;
1075 }
1076
1077 if (tc.m_max_retries > TESTCASE_RETRIES_THRESHOLD_WARNING)
1078 g_logger.warning(
1079 "No of retries should be less than or equal to %d for test '%s'",
1080 TESTCASE_RETRIES_THRESHOLD_WARNING, tc.m_name.c_str());
1081
1082 tc.m_behaviour_on_failure = (FailureMode)g_default_behaviour_on_failure;
1083 if (p.get("on-failure", &str)) {
1084 std::map<std::string, FailureMode> failure_mode_values = {
1085 {"Restart", FailureMode::Restart},
1086 {"Abort", FailureMode::Abort},
1087 {"Skip", FailureMode::Skip},
1088 {"Continue", FailureMode::Continue}};
1089 if (failure_mode_values.find(str) == failure_mode_values.end()) {
1090 g_logger.critical("Invalid Failure mode!!");
1091 return ERR_CORRUPT_TESTCASE;
1092 }
1093 tc.m_behaviour_on_failure = failure_mode_values[str];
1094 used_elements++;
1095 }
1096
1097 if (used_elements != elements) {
1098 g_logger.critical(
1099 "Invalid test file: unknown properties in test case above line: %d",
1100 line);
1101 return ERR_CORRUPT_TESTCASE;
1102 }
1103
1104 return elements;
1105 }
1106
setup_test_case(ProcessManagement & processManagement,atrt_config & config,const atrt_testcase & tc)1107 bool setup_test_case(ProcessManagement &processManagement, atrt_config &config,
1108 const atrt_testcase &tc) {
1109 if (!remove_dir("result", true)) {
1110 g_logger.critical("setup_test_case: Failed to clear result");
1111 return false;
1112 }
1113
1114 for (unsigned i = 0; i < config.m_processes.size(); i++) {
1115 atrt_process &proc = *config.m_processes[i];
1116 if (proc.m_type == atrt_process::AP_NDB_API ||
1117 proc.m_type == atrt_process::AP_CLIENT) {
1118 proc.m_proc.m_path.assign("");
1119 proc.m_proc.m_args.assign("");
1120 }
1121 }
1122
1123 BaseString cmd;
1124 char *p = find_bin_path(tc.m_cmd.m_exe.c_str());
1125 if (p == 0) {
1126 g_logger.critical("Failed to locate '%s'", tc.m_cmd.m_exe.c_str());
1127 return false;
1128 }
1129 cmd.assign(p);
1130 free(p);
1131
1132 for (unsigned i = 0; i < config.m_processes.size(); i++) {
1133 atrt_process &proc = *config.m_processes[i];
1134 if (proc.m_type == tc.m_cmd.m_cmd_type && proc.m_proc.m_path == "") {
1135 proc.m_save.m_proc = proc.m_proc;
1136 proc.m_save.m_saved = true;
1137
1138 proc.m_proc.m_env.appfmt(" ATRT_TIMEOUT=%ld", tc.m_max_time);
1139 if (0) // valgrind
1140 {
1141 proc.m_proc.m_path = "/usr/bin/valgrind";
1142 proc.m_proc.m_args.appfmt("%s %s", cmd.c_str(),
1143 tc.m_cmd.m_args.c_str());
1144 } else {
1145 proc.m_proc.m_path = cmd;
1146 proc.m_proc.m_args.assign(tc.m_cmd.m_args.c_str());
1147 }
1148 if (!tc.m_run_all) break;
1149 }
1150 }
1151
1152 if (tc.m_mysqld_options != "") {
1153 g_logger.info("restarting mysqld with extra options: %s",
1154 tc.m_mysqld_options.c_str());
1155
1156 /**
1157 * Apply testcase specific mysqld options
1158 */
1159 for (unsigned i = 0; i < config.m_processes.size(); i++) {
1160 atrt_process &proc = *config.m_processes[i];
1161 if (proc.m_type == atrt_process::AP_MYSQLD) {
1162 if (!processManagement.stopProcess(proc)) {
1163 return false;
1164 }
1165
1166 if (!processManagement.waitForProcessToStop(proc)) {
1167 return false;
1168 }
1169
1170 proc.m_save.m_proc = proc.m_proc;
1171 proc.m_save.m_saved = true;
1172 proc.m_proc.m_args.appfmt(" %s", tc.m_mysqld_options.c_str());
1173
1174 if (!processManagement.startProcess(proc)) {
1175 return false;
1176 }
1177
1178 if (!connect_mysqld(proc)) {
1179 return false;
1180 }
1181 }
1182 }
1183 }
1184
1185 return true;
1186 }
1187
gather_result(atrt_config & config,int * result)1188 bool gather_result(atrt_config &config, int *result) {
1189 BaseString tmp = g_gather_progname;
1190
1191 for (unsigned i = 0; i < config.m_hosts.size(); i++) {
1192 if (config.m_hosts[i]->m_hostname.length() == 0) continue;
1193
1194 tmp.appfmt(" %s:%s", config.m_hosts[i]->m_hostname.c_str(),
1195 config.m_hosts[i]->m_basedir.c_str());
1196 }
1197
1198 g_logger.debug("system(%s)", tmp.c_str());
1199 const int r1 = sh(tmp.c_str());
1200 if (r1 != 0) {
1201 g_logger.critical("Failed to gather result!");
1202 return false;
1203 }
1204
1205 g_logger.debug("system(%s)", g_analyze_progname);
1206 const int r2 = sh(g_analyze_progname);
1207
1208 if (r2 == -1 || r2 == (127 << 8)) {
1209 g_logger.critical("Failed to analyze results");
1210 return false;
1211 }
1212
1213 *result = r2;
1214 return true;
1215 }
1216
setup_hosts(atrt_config & config)1217 bool setup_hosts(atrt_config &config) {
1218 if (!remove_dir("result", true)) {
1219 g_logger.critical("setup_hosts: Failed to clear result");
1220 return false;
1221 }
1222
1223 for (unsigned i = 0; i < config.m_hosts.size(); i++) {
1224 if (config.m_hosts[i]->m_hostname.length() == 0) continue;
1225 BaseString tmp = g_setup_progname;
1226 tmp.appfmt(" %s %s/ %s/", config.m_hosts[i]->m_hostname.c_str(), g_basedir,
1227 config.m_hosts[i]->m_basedir.c_str());
1228
1229 g_logger.debug("system(%s)", tmp.c_str());
1230 const int r1 = sh(tmp.c_str());
1231 if (r1 != 0) {
1232 g_logger.critical("Failed to setup %s",
1233 config.m_hosts[i]->m_hostname.c_str());
1234 return false;
1235 }
1236 }
1237 return true;
1238 }
1239
do_rsync(const char * dir,const char * dst)1240 static bool do_rsync(const char *dir, const char *dst) {
1241 BaseString tmp = g_setup_progname;
1242 tmp.appfmt(" %s %s/ %s", dst, dir, dir);
1243
1244 g_logger.info("rsyncing %s to %s", dir, dst);
1245 g_logger.debug("system(%s)", tmp.c_str());
1246 const int r1 = sh(tmp.c_str());
1247 if (r1 != 0) {
1248 g_logger.critical("Failed to rsync %s to %s", dir, dst);
1249 return false;
1250 }
1251
1252 return true;
1253 }
1254
deploy(int d,atrt_config & config)1255 bool deploy(int d, atrt_config &config) {
1256 for (unsigned i = 0; i < config.m_hosts.size(); i++) {
1257 if (config.m_hosts[i]->m_hostname.length() == 0) continue;
1258
1259 if (d & 1) {
1260 if (!do_rsync(g_basedir, config.m_hosts[i]->m_hostname.c_str()))
1261 return false;
1262 }
1263
1264 if (d & 2) {
1265 if (!do_rsync(g_prefix0, config.m_hosts[i]->m_hostname.c_str()))
1266 return false;
1267
1268 if (g_prefix1 &&
1269 !do_rsync(g_prefix1, config.m_hosts[i]->m_hostname.c_str()))
1270 return false;
1271 }
1272 }
1273
1274 return true;
1275 }
1276
sshx(atrt_config & config,unsigned mask)1277 bool sshx(atrt_config &config, unsigned mask) {
1278 for (unsigned i = 0; i < config.m_processes.size(); i++) {
1279 atrt_process &proc = *config.m_processes[i];
1280
1281 BaseString tmp;
1282 const char *type = 0;
1283 switch (proc.m_type) {
1284 case atrt_process::AP_NDB_MGMD:
1285 type = (mask & proc.m_type) ? "ndb_mgmd" : 0;
1286 break;
1287 case atrt_process::AP_NDBD:
1288 type = (mask & proc.m_type) ? "ndbd" : 0;
1289 break;
1290 case atrt_process::AP_MYSQLD:
1291 type = (mask & proc.m_type) ? "mysqld" : 0;
1292 break;
1293 case atrt_process::AP_NDB_API:
1294 type = (mask & proc.m_type) ? "ndbapi" : 0;
1295 break;
1296 case atrt_process::AP_CLIENT:
1297 type = (mask & proc.m_type) ? "client" : 0;
1298 break;
1299 default:
1300 type = "<unknown>";
1301 }
1302
1303 if (type == 0) continue;
1304
1305 #ifdef _WIN32
1306 #define SYS_SSH \
1307 "bash '-c echo\"%s(%s) on %s\";" \
1308 "ssh -t %s sh %s/ssh-login.sh' &"
1309 #else
1310 #define SYS_SSH \
1311 "xterm -title \"%s(%s) on %s\"" \
1312 " -e 'ssh -t -X %s sh %s/ssh-login.sh' &"
1313 #endif
1314
1315 tmp.appfmt(SYS_SSH, type, proc.m_cluster->m_name.c_str(),
1316 proc.m_host->m_hostname.c_str(), proc.m_host->m_hostname.c_str(),
1317 proc.m_proc.m_cwd.c_str());
1318
1319 g_logger.debug("system(%s)", tmp.c_str());
1320 const int r1 = sh(tmp.c_str());
1321 if (r1 != 0) {
1322 g_logger.critical("Failed sshx (%s)", tmp.c_str());
1323 return false;
1324 }
1325 NdbSleep_MilliSleep(300); // To prevent xlock problem
1326 }
1327
1328 return true;
1329 }
1330
reset_config(ProcessManagement & processManagement,atrt_config & config)1331 bool reset_config(ProcessManagement &processManagement, atrt_config &config) {
1332 bool changed = false;
1333 for (unsigned i = 0; i < config.m_processes.size(); i++) {
1334 atrt_process &proc = *config.m_processes[i];
1335 if (proc.m_save.m_saved) {
1336 if (proc.m_proc.m_id != -1) {
1337 if (!processManagement.stopProcess(proc)) return false;
1338 if (!processManagement.waitForProcessToStop(proc)) return false;
1339
1340 changed = true;
1341 }
1342
1343 proc.m_save.m_saved = false;
1344 proc.m_proc = proc.m_save.m_proc;
1345 }
1346 }
1347 return changed;
1348 }
1349
find_scripts(const char * atrt_path)1350 bool find_scripts(const char *atrt_path) {
1351 g_logger.info("Locating scripts...");
1352
1353 struct script_path {
1354 const char *name;
1355 const char **path;
1356 };
1357 std::vector<struct script_path> scripts = {
1358 {"atrt-gather-result.sh", &g_gather_progname},
1359 {"atrt-analyze-result.sh", &g_analyze_progname},
1360 {"atrt-setup.sh", &g_setup_progname}};
1361
1362 for (auto &script : scripts) {
1363 BaseString script_full_path;
1364 script_full_path.assfmt("%s/%s", atrt_path, script.name);
1365 if (!File_class::exists(script_full_path.c_str())) {
1366 g_logger.critical("atrt script %s could not be found in %s", script.name,
1367 atrt_path);
1368 return false;
1369 }
1370 *script.path = strdup(script_full_path.c_str());
1371 }
1372 return true;
1373 }
1374
find_config_ini_files()1375 static bool find_config_ini_files() {
1376 g_logger.info("Locating config.ini files...");
1377
1378 BaseString tmp(g_clusters);
1379 Vector<BaseString> clusters;
1380 tmp.split(clusters, ",");
1381
1382 bool found = true;
1383 for (unsigned int i = 0; i < clusters.size(); i++) {
1384 BaseString config_ini_path(g_cwd);
1385 const char *cluster_name = clusters[i].c_str();
1386 config_ini_path.appfmt("%sconfig%s.ini", PATH_SEPARATOR, cluster_name);
1387 to_native(config_ini_path);
1388
1389 if (!exists_file(config_ini_path.c_str())) {
1390 g_logger.critical("Failed to locate '%s'", config_ini_path.c_str());
1391 found = false;
1392 }
1393 }
1394
1395 return found;
1396 }
1397
get_atrt_path(const char * arg)1398 BaseString get_atrt_path(const char *arg) {
1399 char fullPath[FN_REFLEN];
1400 int ret = my_realpath(fullPath, arg, 0);
1401 if (ret == -1) return {};
1402
1403 BaseString path;
1404 char *last_folder_sep = strrchr(fullPath, '/');
1405 if (last_folder_sep != nullptr) {
1406 *last_folder_sep = '\0';
1407 path.assign(fullPath);
1408 }
1409
1410 return path;
1411 }
1412
1413 template class Vector<Vector<SimpleCpcClient::Process> >;
1414 template class Vector<atrt_host *>;
1415 template class Vector<atrt_cluster *>;
1416 template class Vector<atrt_process *>;
1417
check_testcase_file_main(int argc,char ** argv)1418 int check_testcase_file_main(int argc, char **argv) {
1419 bool ok = true;
1420 int argi = 1;
1421 if (strcmp(argv[argi], "--check-testcase-files") == 0) {
1422 argi++;
1423 }
1424 if (argi == argc) {
1425 ok = false;
1426 g_logger.critical("Error: No files to check!\n");
1427 } else
1428 for (; argi < argc; argi++) {
1429 FILE *f = fopen(argv[argi], "r");
1430 if (f == NULL) {
1431 ok = false;
1432 g_logger.critical("Unable to open file: %s (%d: %s)", argv[argi], errno,
1433 strerror(errno));
1434 continue;
1435 }
1436 atrt_testcase tc_dummy;
1437 int line_num = 0;
1438 int ntests = 0;
1439 int num_element_lines;
1440 while ((num_element_lines = read_test_case(f, line_num, tc_dummy)) > 0) {
1441 ntests++;
1442 }
1443 // If line count does not change that indicates end of file.
1444 if (num_element_lines >= 0) {
1445 printf("%s: Contains %d tests in %d lines.\n", argv[argi], ntests,
1446 line_num);
1447 } else {
1448 ok = false;
1449 g_logger.critical("%s: Error at line %d (error %d)\n", argv[argi],
1450 line_num, num_element_lines);
1451 }
1452 fclose(f);
1453 }
1454 return ok ? 0 : 1;
1455 }
1456
print_testcase_file_syntax()1457 void print_testcase_file_syntax() {
1458 printf(
1459 "\n"
1460 "Test cases to run are described in files passed with the\n"
1461 "--testcase-file (-f) option.\n"
1462 "\n"
1463 "A testcase is defined with some properties, one property per line,\n"
1464 "and terminated with exactly one empty line. No other empty lines\n"
1465 "are allowed in the file. Lines starting with # are comments and\n"
1466 "are ignored, note they are not counted as empty lines.\n"
1467 "\n"
1468 "The properties are:\n"
1469 "cmd - Names the test executable. The only mandatory property.\n"
1470 "args - The arguments to test executable.\n"
1471 "max-time - Maximum run time for test in seconds (default 60000).\n"
1472 "type - Declare the type of the test. The only recognized value\n"
1473 " is 'bench' which implies that results are stored also for\n"
1474 " successful tests. Normally if this option is not used\n"
1475 " only results from failed tests will be stored.\n"
1476 "run-all - If 'yes' atrt will start the same command for each defined\n"
1477 " api/mysqld, normally it only starts one instance.\n"
1478 "mysqld - Arguments that atrt will use when starting mysqld.\n"
1479 "cmd-type - If 'mysql' change test process type from ndbapi to client.\n"
1480 "name - Change name of test. Default is given by cmd and args.\n"
1481 "force-cluster-restart - If 'yes' force restart the cluster before\n"
1482 " running test.\n"
1483 "max-retries - Maximum number of retries after test failed.\n"
1484 ""
1485 "\n"
1486 "Example:\n"
1487 "# BASIC FUNCTIONALITY\n"
1488 "max-time: 500\n"
1489 "cmd: testBasic\n"
1490 "args: -n PkRead\n"
1491 "\n"
1492 "# 4k record DD\n"
1493 "max-time: 600\n"
1494 "cmd: flexAsynch\n"
1495 "args: -dd -temp -con 2 -t 8 -r 2 -p 64 -ndbrecord -a 25 -s 40\n"
1496 "type: bench\n"
1497 "\n"
1498 "# sql\n"
1499 "max-time: 600\n"
1500 "cmd: ndb-sql-perf.sh\n"
1501 "args: ndb-sql-perf-select.sh t1 1 64\n"
1502 "mysqld: --ndb-cluster-connection-pool=1\n"
1503 "type: bench\n"
1504 "cmd-type: mysql\n"
1505 "\n");
1506 }
1507