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