1 /*
2    Copyright (c) 2003, 2021, Oracle and/or its affiliates.
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 
26 #ifdef _WIN32
27 #define DEFAULT_PREFIX "c:/atrt"
28 #endif
29 
30 #include "atrt.hpp"
31 #include <NdbOut.hpp>
32 #include <NdbAutoPtr.hpp>
33 
34 #include <SysLogHandler.hpp>
35 #include <FileLogHandler.hpp>
36 
37 #include <NdbSleep.h>
38 
39 #define PATH_SEPARATOR DIR_SEPARATOR
40 
41 /** Global variables */
42 static const char progname[] = "ndb_atrt";
43 static const char * g_gather_progname = "atrt-gather-result.sh";
44 static const char * g_analyze_progname = "atrt-analyze-result.sh";
45 static const char * g_setup_progname = "atrt-setup.sh";
46 
47 static const char * g_log_filename = 0;
48 static const char * g_test_case_filename = 0;
49 static const char * g_report_filename = 0;
50 
51 static int g_do_setup = 0;
52 static int g_do_deploy = 0;
53 static int g_do_sshx = 0;
54 static int g_do_start = 0;
55 static int g_do_quit = 0;
56 
57 static int g_help = 0;
58 static int g_verbosity = 1;
59 static FILE * g_report_file = 0;
60 static FILE * g_test_case_file = stdin;
61 static int g_mode = 0;
62 
63 Logger g_logger;
64 atrt_config g_config;
65 const char * g_user = 0;
66 int          g_baseport = 10000;
67 int          g_fqpn = 0;
68 int          g_fix_nodeid= 0;
69 int          g_default_ports = 0;
70 int          g_mt = 0;
71 int          g_mt_rr = 0;
72 int          g_restart = 0;
73 
74 const char * g_cwd = 0;
75 const char * g_basedir = 0;
76 const char * g_my_cnf = 0;
77 const char * g_prefix = 0;
78 const char * g_prefix1 = 0;
79 const char * g_clusters = 0;
80 BaseString g_replicate;
81 const char *save_file = 0;
82 const char *save_group_suffix = 0;
83 const char * g_dummy;
84 char * g_env_path = 0;
85 const char* g_mysqld_host = 0;
86 
87 const char * g_ndb_mgmd_bin_path = 0;
88 const char * g_ndbd_bin_path = 0;
89 const char * g_ndbmtd_bin_path = 0;
90 const char * g_mysqld_bin_path = 0;
91 const char * g_mysql_install_db_bin_path = 0;
92 const char * g_libmysqlclient_so_path = 0;
93 
94 static struct
95 {
96   bool is_required;
97   const char * exe;
98   const char ** var;
99 } g_binaries[] = {
100   { true,  "ndb_mgmd",          &g_ndb_mgmd_bin_path},
101   { true,  "ndbd",              &g_ndbd_bin_path },
102   { false, "ndbmtd",            &g_ndbmtd_bin_path },
103   { true,  "mysqld",            &g_mysqld_bin_path },
104   { true,  "mysql_install_db",  &g_mysql_install_db_bin_path },
105 #if defined(__MACH__)
106   { true,  "libmysqlclient.dylib", &g_libmysqlclient_so_path },
107 #else
108   { true,  "libmysqlclient.so", &g_libmysqlclient_so_path },
109 #endif
110   { true, 0, 0 }
111 };
112 
113 const char *
114 g_search_path[] =
115 {
116   "bin",
117   "libexec",
118   "sbin",
119   "scripts",
120   "lib",
121   "lib/mysql",
122   0
123 };
124 static bool find_binaries();
125 
126 static struct my_option g_options[] =
127 {
128   { "help", '?', "Display this help and exit.",
129     (uchar **) &g_help, (uchar **) &g_help,
130     0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
131   { "version", 'V', "Output version information and exit.", 0, 0, 0,
132     GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
133   { "clusters", 256, "Cluster",
134     (uchar **) &g_clusters, (uchar **) &g_clusters,
135     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
136   { "mysqld", 256, "atrt mysqld",
137     (uchar **) &g_mysqld_host, (uchar **) &g_mysqld_host,
138     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
139   { "replicate", 1024, "replicate",
140     (uchar **) &g_dummy, (uchar **) &g_dummy,
141     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
142   { "log-file", 256, "log-file",
143     (uchar **) &g_log_filename, (uchar **) &g_log_filename,
144     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
145   { "testcase-file", 'f', "testcase-file",
146     (uchar **) &g_test_case_filename, (uchar **) &g_test_case_filename,
147     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
148   { "report-file", 'r', "report-file",
149     (uchar **) &g_report_filename, (uchar **) &g_report_filename,
150     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
151   { "basedir", 256, "Base path",
152     (uchar **) &g_basedir, (uchar **) &g_basedir,
153     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
154   { "baseport", 256, "Base port",
155     (uchar **) &g_baseport, (uchar **) &g_baseport,
156     0, GET_INT, REQUIRED_ARG, g_baseport, 0, 0, 0, 0, 0},
157   { "prefix", 256, "mysql install dir",
158     (uchar **) &g_prefix, (uchar **) &g_prefix,
159     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
160   { "prefix1", 256, "mysql install dir 1",
161     (uchar **) &g_prefix1, (uchar **) &g_prefix1,
162     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
163   { "verbose", 'v', "Verbosity",
164     (uchar **) &g_verbosity, (uchar **) &g_verbosity,
165     0, GET_INT, REQUIRED_ARG, g_verbosity, 0, 0, 0, 0, 0},
166   { "configure", 256, "configure",
167     (uchar **) &g_do_setup, (uchar **) &g_do_setup,
168     0, GET_INT, REQUIRED_ARG, g_do_setup, 0, 0, 0, 0, 0 },
169   { "deploy", 256, "deploy",
170     (uchar **) &g_do_deploy, (uchar **) &g_do_deploy,
171     0, GET_INT, REQUIRED_ARG, g_do_deploy, 0, 0, 0, 0, 0 },
172   { "sshx", 256, "sshx",
173     (uchar **) &g_do_sshx, (uchar **) &g_do_sshx,
174     0, GET_INT, REQUIRED_ARG, g_do_sshx, 0, 0, 0, 0, 0 },
175   { "start", 256, "start",
176     (uchar **) &g_do_start, (uchar **) &g_do_start,
177     0, GET_INT, REQUIRED_ARG, g_do_start, 0, 0, 0, 0, 0 },
178   { "fqpn", 256, "Fully qualified path-names ",
179     (uchar **) &g_fqpn, (uchar **) &g_fqpn,
180     0, GET_INT, REQUIRED_ARG, g_fqpn, 0, 0, 0, 0, 0 },
181   { "fix-nodeid", 256, "Fix nodeid for each started process ",
182     (uchar **) &g_fix_nodeid, (uchar **) &g_fix_nodeid,
183     0, GET_INT, REQUIRED_ARG, g_fqpn, 0, 0, 0, 0, 0 },
184   { "default-ports", 256, "Use default ports when possible",
185     (uchar **) &g_default_ports, (uchar **) &g_default_ports,
186     0, GET_INT, REQUIRED_ARG, g_default_ports, 0, 0, 0, 0, 0 },
187   { "mode", 256, "Mode 0=interactive 1=regression 2=bench",
188     (uchar **) &g_mode, (uchar **) &g_mode,
189     0, GET_INT, REQUIRED_ARG, g_mode, 0, 0, 0, 0, 0 },
190   { "quit", 256, "Quit before starting tests",
191     (uchar **) &g_do_quit, (uchar **) &g_do_quit,
192     0, GET_BOOL, NO_ARG, g_do_quit, 0, 0, 0, 0, 0 },
193   { "mt", 256, "Use ndbmtd (0 = never, 1 = round-robin, 2 = only)",
194     (uchar **) &g_mt, (uchar **) &g_mt,
195     0, GET_INT, REQUIRED_ARG, g_mt, 0, 0, 0, 0, 0 },
196   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
197 };
198 
199 const int p_ndb     = atrt_process::AP_NDB_MGMD | atrt_process::AP_NDBD;
200 const int p_servers = atrt_process::AP_MYSQLD;
201 const int p_clients = atrt_process::AP_CLIENT | atrt_process::AP_NDB_API;
202 
203 static int check_testcase_file_main(int argc, char ** argv);
204 static void print_testcase_file_syntax();
205 
206 int
main(int argc,char ** argv)207 main(int argc, char ** argv)
208 {
209   // If program is called with --check-testcase-files as first option
210   // it is assumed that the rest of command line arguments are
211   // testcase-filenames and those files will be syntax checked.
212   if (argc >= 2 && strcmp(argv[1], "--check-testcase-files") == 0)
213   {
214     exit(check_testcase_file_main(argc, argv));
215   }
216 
217   ndb_init();
218 
219   bool restart = true;
220   int lineno = 1;
221   int test_no = 1;
222   int return_code = 1;
223 
224   g_logger.setCategory(progname);
225   g_logger.enable(Logger::LL_ALL);
226   g_logger.createConsoleHandler();
227 
228   if(!parse_args(argc, argv))
229   {
230     g_logger.critical("Failed to parse arguments");
231     goto end;
232   }
233 
234   g_logger.info("Starting...");
235 
236   if (!find_binaries())
237   {
238     goto end;
239   }
240 
241   g_config.m_generated = false;
242   g_config.m_replication = g_replicate;
243   if (!setup_config(g_config, g_mysqld_host))
244   {
245     g_logger.critical("Failed to setup configuration");
246     goto end;
247   }
248 
249   if (!configure(g_config, g_do_setup))
250   {
251     g_logger.critical("Failed to configure");
252     goto end;
253   }
254 
255   g_logger.info("Setting up directories...");
256   if (!setup_directories(g_config, g_do_setup))
257   {
258     g_logger.critical("Failed to set up directories");
259     goto end;
260   }
261 
262   if (g_do_setup)
263   {
264     g_logger.info("Setting up files...");
265     if (!setup_files(g_config, g_do_setup, g_do_sshx))
266     {
267       g_logger.critical("Failed to set up files");
268       goto end;
269     }
270   }
271 
272   if (g_do_deploy)
273   {
274     if (!deploy(g_do_deploy, g_config))
275     {
276       g_logger.critical("Failed to deploy");
277       goto end;
278     }
279   }
280 
281   if (g_do_quit)
282   {
283     return_code = 0;
284     goto end;
285   }
286 
287   if(!setup_hosts(g_config))
288   {
289     g_logger.critical("Failed to setup hosts");
290     goto end;
291   }
292 
293   if (g_do_sshx)
294   {
295     g_logger.info("Starting xterm-ssh");
296     if (!sshx(g_config, g_do_sshx))
297     {
298       g_logger.critical("Failed to start xterm-ssh");
299       goto end;
300     }
301 
302     g_logger.info("Done...sleeping");
303     while(true)
304     {
305       if (!do_command(g_config))
306       {
307         g_logger.critical("Failed to do ssh command");
308         goto end;
309       }
310 
311       NdbSleep_SecSleep(1);
312     }
313     return_code = 0;
314     goto end;
315   }
316 
317   g_logger.info("Connecting to hosts...");
318   if(!connect_hosts(g_config))
319   {
320     g_logger.critical("Failed to connect to CPCD on hosts");
321     goto end;
322   }
323 
324 #ifndef _WIN32
325   if (g_do_start && !g_test_case_filename)
326   {
327     g_logger.info("Starting server processes: %x", g_do_start);
328     if (!start(g_config, g_do_start))
329     {
330       g_logger.critical("Failed to start server processes");
331       goto end;
332     }
333 
334     if (!setup_db(g_config))
335     {
336       g_logger.critical("Failed to setup database");
337       goto end;
338     }
339 
340     g_logger.info("Done...sleeping");
341     while(true)
342     {
343       if (!do_command(g_config))
344       {
345         g_logger.info("Exiting");
346         goto end;
347       }
348 
349       NdbSleep_SecSleep(1);
350     }
351     return_code = 0;
352     goto end;
353   }
354 #endif
355 
356   /**
357    * Main loop
358    */
359   g_logger.debug("Entering main loop");
360   while(!feof(g_test_case_file))
361   {
362     /**
363      * Do we need to restart ndb
364      */
365     if(restart)
366     {
367       restart = false;
368       g_logger.info("(Re)starting server processes...");
369 
370       if(!stop_processes(g_config, ~0))
371       {
372         g_logger.critical("Failed to stop all processes");
373         goto end;
374       }
375 
376       if (!setup_directories(g_config, 2))
377       {
378         g_logger.critical("Failed to setup directories");
379         goto end;
380       }
381 
382       if (!setup_files(g_config, 2, 1))
383       {
384         g_logger.critical("Failed to setup files");
385         goto end;
386       }
387 
388       if(!setup_hosts(g_config))
389       {
390         g_logger.critical("Failed to setup hosts");
391         goto end;
392       }
393 
394       g_logger.debug("Setup complete, starting servers");
395       if (!start(g_config, p_ndb | p_servers))
396       {
397         g_logger.critical("Failed to start server processes");
398         g_logger.info("Gathering logs and saving them as test %u", test_no);
399 
400         int tmp;
401         if(!gather_result(g_config, &tmp))
402         {
403           g_logger.critical("Failed to gather results");
404           goto end;
405         }
406 
407         if(g_report_file != 0)
408         {
409           fprintf(g_report_file, "%s ; %d ; %d ; %d\n",
410                   "start servers", test_no, ERR_FAILED_TO_START, 0);
411           fflush(g_report_file);
412         }
413 
414         BaseString resdir;
415         resdir.assfmt("result.%d", test_no);
416         remove_dir(resdir.c_str(), true);
417 
418         if(rename("result", resdir.c_str()) != 0)
419         {
420           g_logger.critical("Failed to rename %s as %s",
421                             "result", resdir.c_str());
422           goto end;
423         }
424         goto end;
425       }
426 
427       if (!setup_db(g_config))
428       {
429         g_logger.critical("Failed to setup database");
430         goto end;
431       }
432 
433       g_logger.info("All servers start completed");
434     }
435 
436     // const int start_line = lineno;
437     atrt_testcase test_case;
438     if(!read_test_case(g_test_case_file, test_case, lineno))
439     {
440       g_logger.critical("Corrupt testcase at line %d)", lineno);
441       goto end;
442 
443     }
444     g_logger.info("#%d - %s",
445 		  test_no,
446 		  test_case.m_name.c_str());
447 
448     // Assign processes to programs
449     if (!setup_test_case(g_config, test_case))
450     {
451       g_logger.critical("Failed to setup test case");
452       goto end;
453     }
454 
455     if(!start_processes(g_config, p_clients))
456     {
457       g_logger.critical("Failed to start client processes");
458       goto end;
459     }
460 
461     int result = 0;
462 
463     const time_t start = time(0);
464     time_t now = start;
465     do
466     {
467       if(!update_status(g_config, atrt_process::AP_ALL))
468       {
469         g_logger.critical("Failed to get updated status for all processes");
470         goto end;
471       }
472 
473       if(is_running(g_config, p_ndb) != 2)
474       {
475 	result = ERR_NDB_FAILED;
476 	break;
477       }
478 
479       if(is_running(g_config, p_servers) != 2)
480       {
481 	result = ERR_SERVERS_FAILED;
482 	break;
483       }
484 
485       if(is_running(g_config, p_clients) == 0)
486       {
487 	break;
488       }
489 
490       if (!do_command(g_config))
491       {
492         result = ERR_COMMAND_FAILED;
493 	break;
494       }
495 
496       now = time(0);
497       if(now  > (start + test_case.m_max_time))
498       {
499         g_logger.debug("Timed out");
500 	result = ERR_MAX_TIME_ELAPSED;
501         g_logger.info("Timeout '%s' after %ld seconds", test_case.m_name.c_str(), test_case.m_max_time);
502 	break;
503       }
504       NdbSleep_SecSleep(1);
505     } while(true);
506 
507     const time_t elapsed = time(0) - start;
508 
509     if(!stop_processes(g_config, p_clients))
510     {
511       g_logger.critical("Failed to stop client processes");
512       goto end;
513     }
514 
515     int tmp, *rp = result ? &tmp : &result;
516     if(!gather_result(g_config, rp))
517     {
518       g_logger.critical("Failed to gather result after test run");
519       goto end;
520     }
521 
522     g_logger.info("#%d %s(%d)",
523 		  test_no,
524 		  (result == 0 ? "OK" : "FAILED"), result);
525 
526     if(g_report_file != 0)
527     {
528       fprintf(g_report_file, "%s ; %d ; %d ; %ld\n",
529 	      test_case.m_name.c_str(), test_no, result, elapsed);
530       fflush(g_report_file);
531     }
532 
533     if(g_mode == 0 && result)
534     {
535       g_logger.info
536 	("Encountered failed test in interactive mode - terminating");
537       break;
538     }
539 
540     BaseString resdir;
541     resdir.assfmt("result.%d", test_no);
542     remove_dir(resdir.c_str(), true);
543 
544     if(test_case.m_report || g_mode == 2 || (g_mode && result))
545     {
546       if(rename("result", resdir.c_str()) != 0)
547       {
548 	g_logger.critical("Failed to rename %s as %s",
549 			  "result", resdir.c_str());
550 	goto end;
551       }
552     }
553     else
554     {
555       remove_dir("result", true);
556     }
557 
558     if (reset_config(g_config))
559     {
560       restart = true;
561     }
562 
563     if(result != 0)
564     {
565       restart = true;
566     }
567     test_no++;
568   }
569   return_code = 0;
570 
571  end:
572   if(return_code != 0 && g_report_file != 0)
573   {
574     fprintf(g_report_file, "%s ; %d ; %d ; %d\n",
575             "critical error", test_no, ERR_FAILED_TO_START, 0);
576     fflush(g_report_file);
577   }
578   if(g_report_file != 0){
579     fclose(g_report_file);
580     g_report_file = 0;
581   }
582 
583   if(g_test_case_file != 0 && g_test_case_file != stdin){
584     fclose(g_test_case_file);
585     g_test_case_file = 0;
586   }
587 
588   g_logger.info("Stopping all processes, result: %d", return_code);
589   stop_processes(g_config, atrt_process::AP_ALL);
590   return return_code;
591 }
592 
593 extern "C"
594 my_bool
get_one_option(int arg,const struct my_option * opt,char * value)595 get_one_option(int arg, const struct my_option * opt, char * value)
596 {
597   if (arg == 1024)
598   {
599     if (g_replicate.length())
600       g_replicate.append(";");
601     g_replicate.append(value);
602     return 0;
603   }
604   return 0;
605 }
606 
607 bool
parse_args(int argc,char ** argv)608 parse_args(int argc, char** argv)
609 {
610   bool fail_after_help = false;
611   char buf[2048];
612   if (getcwd(buf, sizeof(buf)) == 0)
613   {
614     g_logger.error("Unable to get current working directory");
615     return false;
616   }
617   g_cwd = strdup(buf);
618 
619   struct stat sbuf;
620   BaseString mycnf;
621   mycnf.append(g_cwd);
622   mycnf.append(DIR_SEPARATOR);
623 
624   if (argc > 1 && lstat(argv[argc-1], &sbuf) == 0)
625   {
626     mycnf.append(argv[argc-1]);
627   }
628   else
629   {
630     mycnf.append("my.cnf");
631     if (lstat(mycnf.c_str(), &sbuf) != 0)
632     {
633       g_logger.error("Could not find out which config file to use! "
634                      "Pass it as last argument to atrt: 'atrt <config file>' "
635                      "(default: '%s')", mycnf.c_str());
636       fail_after_help = true;
637     }
638   }
639 
640   to_fwd_slashes((char*)g_cwd);
641 
642   g_logger.info("Bootstrapping using %s", mycnf.c_str());
643 
644   const char *groups[] = { "atrt", 0 };
645   int ret = load_defaults(mycnf.c_str(), groups, &argc, &argv);
646 
647   if (ret)
648   {
649     g_logger.error("Failed to load defaults, returned (%d)",ret);
650     return false;
651   }
652 
653   save_file = my_defaults_file;
654   save_group_suffix = my_defaults_group_suffix;
655 
656   if (my_defaults_extra_file)
657   {
658     g_logger.error("--defaults-extra-file(%s) is not supported...",
659 		   my_defaults_extra_file);
660     return false;
661   }
662 
663   ret =  handle_options(&argc, &argv, g_options, get_one_option);
664   if (ret)
665   {
666     g_logger.error("handle_options failed, ret: %d, argc: %d, *argv: '%s'",
667                     ret, argc, *argv);
668     return false;
669   }
670 
671   if (argc >= 2)
672   {
673     const char * arg = argv[argc-2];
674     while(* arg)
675     {
676       switch(* arg){
677       case 'c':
678 	g_do_setup = (g_do_setup == 0) ? 1 : g_do_setup;
679 	break;
680       case 'C':
681 	g_do_setup = 2;
682 	break;
683       case 'd':
684 	g_do_deploy = 3;
685 	break;
686       case 'D':
687         g_do_deploy = 2; // only binaries
688         break;
689       case 'x':
690 	g_do_sshx = atrt_process::AP_CLIENT | atrt_process::AP_NDB_API;
691 	break;
692       case 'X':
693 	g_do_sshx = atrt_process::AP_ALL;
694 	break;
695       case 's':
696 	g_do_start = p_ndb;
697 	break;
698       case 'S':
699 	g_do_start = p_ndb | p_servers;
700 	break;
701       case 'f':
702 	g_fqpn = 1;
703 	break;
704       case 'z':
705 	g_fix_nodeid = 1;
706 	break;
707       case 'q':
708 	g_do_quit = 1;
709 	break;
710       case 'r':
711         g_restart = 1;
712         break;
713       default:
714 	g_logger.error("Unknown switch '%c'", *arg);
715 	return false;
716       }
717       arg++;
718     }
719   }
720 
721   if(g_log_filename != 0)
722   {
723     g_logger.removeConsoleHandler();
724     g_logger.addHandler(new FileLogHandler(g_log_filename));
725   }
726 
727   {
728     int tmp = Logger::LL_WARNING - g_verbosity;
729     tmp = (tmp < Logger::LL_DEBUG ? Logger::LL_DEBUG : tmp);
730     g_logger.disable(Logger::LL_ALL);
731     g_logger.enable(Logger::LL_ON);
732     g_logger.enable((Logger::LoggerLevel)tmp, Logger::LL_ALERT);
733   }
734 
735   if(!g_basedir)
736   {
737     g_basedir = g_cwd;
738     g_logger.info("basedir not specified, using %s", g_basedir);
739   }
740   else
741   {
742     g_logger.info("basedir, %s", g_basedir);
743   }
744 
745   if (!g_prefix)
746   {
747     g_prefix = DEFAULT_PREFIX;
748   }
749 
750   /**
751    * Add path to atrt-*.sh
752    */
753   {
754     BaseString tmp;
755     const char* env = getenv("PATH");
756     if (env && strlen(env))
757     {
758       tmp.assfmt("PATH=%s:%s/mysql-test/ndb",
759 		 env, g_prefix);
760     }
761     else
762     {
763       tmp.assfmt("PATH=%s/mysql-test/ndb", g_prefix);
764     }
765     to_native(tmp);
766     g_env_path = strdup(tmp.c_str());
767     putenv(g_env_path);
768   }
769 
770   if (g_help)
771   {
772     my_print_help(g_options);
773     my_print_variables(g_options);
774     print_testcase_file_syntax();
775     return 0;
776   }
777   if (fail_after_help)
778   {
779     return false;
780   }
781 
782   if(g_test_case_filename)
783   {
784     g_test_case_file = fopen(g_test_case_filename, "r");
785     if(g_test_case_file == 0)
786     {
787       g_logger.critical("Unable to open file: %s", g_test_case_filename);
788       return false;
789     }
790     if (g_do_setup == 0)
791       g_do_setup = 2;
792 
793     if (g_do_start == 0)
794       g_do_start = p_ndb | p_servers;
795 
796     if (g_mode == 0)
797       g_mode = 1;
798 
799     if (g_do_sshx)
800     {
801       g_logger.critical("ssx specified...not possible with testfile");
802       return false;
803     }
804   }
805   else {
806     g_logger.info("No test case file given with -f <test file>, "
807                   "running in interactive mode from stdin");
808   }
809 
810   if (g_do_setup == 0)
811   {
812     BaseString tmp;
813     tmp.append(g_basedir);
814     tmp.append(PATH_SEPARATOR);
815     tmp.append("my.cnf");
816     if (lstat(tmp.c_str(), &sbuf) != 0)
817     {
818       g_logger.error("Could not find a my.cnf file in the basedir '%s', "
819                      "you probably need to configure it with "
820                      "'atrt --configure=1 <config_file>'", g_basedir);
821       return false;
822     }
823 
824     if (!S_ISREG(sbuf.st_mode))
825     {
826       g_logger.error("%s is not a regular file", tmp.c_str());
827       return false;
828     }
829 
830     g_my_cnf = strdup(tmp.c_str());
831     g_logger.info("Using %s", tmp.c_str());
832   }
833   else
834   {
835     g_my_cnf = strdup(mycnf.c_str());
836   }
837 
838   g_logger.info("Using --prefix=\"%s\"", g_prefix);
839 
840   if (g_prefix1)
841   {
842     g_logger.info("Using --prefix1=\"%s\"", g_prefix1);
843   }
844 
845 
846 
847   if(g_report_filename)
848   {
849     g_report_file = fopen(g_report_filename, "w");
850     if(g_report_file == 0)
851     {
852       g_logger.critical("Unable to create report file: %s", g_report_filename);
853       return false;
854     }
855   }
856 
857   if (g_clusters == 0)
858   {
859     g_logger.critical("No clusters specified");
860     return false;
861   }
862 
863   /* Read username from environment, default to sakila */
864   g_user = strdup(getenv("LOGNAME"));
865   if (g_user == 0)
866   {
867     g_user = "sakila";
868     g_logger.info("No default user specified, will use 'sakila'.");
869     g_logger.info("Please set LOGNAME environment variable for other username");
870   }
871   return true;
872 }
873 
874 bool
connect_hosts(atrt_config & config)875 connect_hosts(atrt_config& config){
876   for(unsigned i = 0; i<config.m_hosts.size(); i++)
877   {
878     if (config.m_hosts[i]->m_hostname.length() == 0)
879       continue;
880 
881     if(config.m_hosts[i]->m_cpcd->connect() != 0){
882       g_logger.error("Unable to connect to cpc %s:%d",
883 		     config.m_hosts[i]->m_cpcd->getHost(),
884 		     config.m_hosts[i]->m_cpcd->getPort());
885       return false;
886     }
887     g_logger.debug("Connected to %s:%d",
888 		   config.m_hosts[i]->m_cpcd->getHost(),
889 		   config.m_hosts[i]->m_cpcd->getPort());
890   }
891 
892   return true;
893 }
894 
895 bool
connect_ndb_mgm(atrt_process & proc)896 connect_ndb_mgm(atrt_process & proc){
897   NdbMgmHandle handle = ndb_mgm_create_handle();
898   if(handle == 0){
899     g_logger.critical("Unable to create mgm handle");
900     return false;
901   }
902   BaseString tmp = proc.m_host->m_hostname;
903   const char * val;
904   proc.m_options.m_loaded.get("--PortNumber=", &val);
905   tmp.appfmt(":%s", val);
906 
907   if (ndb_mgm_set_connectstring(handle,tmp.c_str()))
908   {
909     g_logger.critical("Unable to create parse connectstring");
910     return false;
911   }
912 
913   if(ndb_mgm_connect(handle, 30, 1, 0) != -1)
914   {
915     proc.m_ndb_mgm_handle = handle;
916     return true;
917   }
918 
919   g_logger.critical("Unable to connect to ndb mgm %s", tmp.c_str());
920   return false;
921 }
922 
923 bool
connect_ndb_mgm(atrt_config & config)924 connect_ndb_mgm(atrt_config& config){
925   for(unsigned i = 0; i<config.m_processes.size(); i++){
926     atrt_process & proc = *config.m_processes[i];
927     if((proc.m_type & atrt_process::AP_NDB_MGMD) != 0){
928       if(!connect_ndb_mgm(proc)){
929 	return false;
930       }
931     }
932   }
933 
934   return true;
935 }
936 
remap(int i)937 static int remap(int i){
938   if(i == NDB_MGM_NODE_STATUS_NO_CONTACT) return NDB_MGM_NODE_STATUS_UNKNOWN;
939   if(i == NDB_MGM_NODE_STATUS_UNKNOWN) return NDB_MGM_NODE_STATUS_NO_CONTACT;
940   return i;
941 }
942 
943 bool
wait_ndb(atrt_config & config,int goal)944 wait_ndb(atrt_config& config, int goal){
945 
946   goal = remap(goal);
947 
948   size_t cnt = 0;
949   for (unsigned i = 0; i<config.m_clusters.size(); i++)
950   {
951     atrt_cluster* cluster = config.m_clusters[i];
952 
953     if (strcmp(cluster->m_name.c_str(), ".atrt") == 0)
954     {
955       /**
956        * skip atrt mysql
957        */
958       cnt++;
959       continue;
960     }
961 
962     /**
963      * Get mgm handle for cluster
964      */
965     NdbMgmHandle handle = 0;
966     for(unsigned j = 0; j<cluster->m_processes.size(); j++){
967       atrt_process & proc = *cluster->m_processes[j];
968       if((proc.m_type & atrt_process::AP_NDB_MGMD) != 0){
969 	handle = proc.m_ndb_mgm_handle;
970 	break;
971       }
972     }
973 
974     if(handle == 0){
975       return true;
976     }
977 
978     if(goal == NDB_MGM_NODE_STATUS_STARTED){
979       /**
980        * 1) wait NOT_STARTED
981        * 2) send start
982        * 3) wait STARTED
983        */
984       if(!wait_ndb(config, NDB_MGM_NODE_STATUS_NOT_STARTED))
985 	return false;
986 
987       ndb_mgm_start(handle, 0, 0);
988     }
989 
990     struct ndb_mgm_cluster_state * state;
991 
992     time_t now = time(0);
993     time_t end = now + 360;
994     int min = remap(NDB_MGM_NODE_STATUS_NO_CONTACT);
995     int min2 = goal;
996 
997     while(now < end){
998       /**
999        * 1) retreive current state
1000        */
1001       state = 0;
1002       do {
1003 	state = ndb_mgm_get_status(handle);
1004 	if(state == 0){
1005 	  const int err = ndb_mgm_get_latest_error(handle);
1006 	  g_logger.error("Unable to poll db state: %d %s %s",
1007 			 ndb_mgm_get_latest_error(handle),
1008 			 ndb_mgm_get_latest_error_msg(handle),
1009 			 ndb_mgm_get_latest_error_desc(handle));
1010 	  if(err == NDB_MGM_SERVER_NOT_CONNECTED && connect_ndb_mgm(config)){
1011 	    g_logger.error("Reconnected...");
1012 	    continue;
1013 	  }
1014 	  return false;
1015 	}
1016       } while(state == 0);
1017       NdbAutoPtr<void> tmp(state);
1018 
1019       min2 = goal;
1020       for(int j = 0; j<state->no_of_nodes; j++){
1021 	if(state->node_states[j].node_type == NDB_MGM_NODE_TYPE_NDB){
1022 	  const int s = remap(state->node_states[j].node_status);
1023 	  min2 = (min2 < s ? min2 : s );
1024 
1025 	  if(s < remap(NDB_MGM_NODE_STATUS_NO_CONTACT) ||
1026 	     s > NDB_MGM_NODE_STATUS_STARTED){
1027 	    g_logger.critical("Strange DB status during start: %d %d",
1028 			      j, min2);
1029 	    return false;
1030 	  }
1031 
1032 	  if(min2 < min){
1033 	    g_logger.critical("wait ndb failed node: %d %d %d %d",
1034 			      state->node_states[j].node_id, min, min2, goal);
1035 	  }
1036 	}
1037       }
1038 
1039       if(min2 < min){
1040 	g_logger.critical("wait ndb failed %d %d %d", min, min2, goal);
1041 	return false;
1042       }
1043 
1044       if(min2 == goal){
1045 	cnt++;
1046 	goto next;
1047       }
1048 
1049       min = min2;
1050       now = time(0);
1051     }
1052 
1053     g_logger.critical("wait ndb timed out %d %d %d", min, min2, goal);
1054     break;
1055 
1056 next:
1057     ;
1058   }
1059 
1060   return cnt == config.m_clusters.size();
1061 }
1062 
1063 bool
start_process(atrt_process & proc)1064 start_process(atrt_process & proc){
1065   if(proc.m_proc.m_id != -1){
1066     g_logger.critical("starting already started process: %u",
1067                       (unsigned)proc.m_index);
1068     return false;
1069   }
1070 
1071   BaseString tmp = g_setup_progname;
1072   tmp.appfmt(" %s %s/ %s",
1073 	     proc.m_host->m_hostname.c_str(),
1074 	     proc.m_proc.m_cwd.c_str(),
1075 	     proc.m_proc.m_cwd.c_str());
1076 
1077   g_logger.debug("system(%s)", tmp.c_str());
1078   const int r1 = sh(tmp.c_str());
1079   if(r1 != 0)
1080   {
1081     g_logger.critical("Failed to setup process");
1082     return false;
1083   }
1084 
1085   {
1086     Properties reply;
1087     if(proc.m_host->m_cpcd->define_process(proc.m_proc, reply) != 0){
1088       BaseString msg;
1089       reply.get("errormessage", msg);
1090       g_logger.error("Unable to define process: %s", msg.c_str());
1091       return false;
1092     }
1093   }
1094   {
1095     Properties reply;
1096     if(proc.m_host->m_cpcd->start_process(proc.m_proc.m_id, reply) != 0){
1097       BaseString msg;
1098       reply.get("errormessage", msg);
1099       g_logger.error("Unable to start process: %s", msg.c_str());
1100       return false;
1101     }
1102   }
1103   return true;
1104 }
1105 
1106 bool
start_processes(atrt_config & config,int types)1107 start_processes(atrt_config& config, int types){
1108   for(unsigned i = 0; i<config.m_processes.size(); i++){
1109     atrt_process & proc = *config.m_processes[i];
1110     if(IF_WIN(!(proc.m_type & atrt_process::AP_MYSQLD), 1)
1111        && (types & proc.m_type) != 0 && proc.m_proc.m_path != ""){
1112       if(!start_process(proc)){
1113 	return false;
1114       }
1115     }
1116   }
1117   return true;
1118 }
1119 
1120 bool
stop_process(atrt_process & proc)1121 stop_process(atrt_process & proc){
1122   if(proc.m_proc.m_id == -1){
1123     return true;
1124   }
1125 
1126   if (proc.m_type == atrt_process::AP_MYSQLD)
1127   {
1128     disconnect_mysqld(proc);
1129   }
1130 
1131   {
1132     Properties reply;
1133     if(proc.m_host->m_cpcd->stop_process(proc.m_proc.m_id, reply) != 0){
1134       Uint32 status;
1135       reply.get("status", &status);
1136       if(status != 4){
1137 	BaseString msg;
1138 	reply.get("errormessage", msg);
1139 	g_logger.error("Unable to stop process: %s(%d)", msg.c_str(), status);
1140 	return false;
1141       }
1142     }
1143   }
1144   {
1145     Properties reply;
1146     if(proc.m_host->m_cpcd->undefine_process(proc.m_proc.m_id, reply) != 0){
1147       BaseString msg;
1148       reply.get("errormessage", msg);
1149       g_logger.error("Unable to undefine process: %s", msg.c_str());
1150       return false;
1151     }
1152     proc.m_proc.m_id = -1;
1153   }
1154   return true;
1155 }
1156 
1157 bool
stop_processes(atrt_config & config,int types)1158 stop_processes(atrt_config& config, int types){
1159   for(unsigned i = 0; i<config.m_processes.size(); i++){
1160     atrt_process & proc = *config.m_processes[i];
1161     if((types & proc.m_type) != 0){
1162       if(!stop_process(proc)){
1163 	return false;
1164       }
1165     }
1166   }
1167   return true;
1168 }
1169 
1170 bool
update_status(atrt_config & config,int)1171 update_status(atrt_config& config, int){
1172 
1173   Vector<Vector<SimpleCpcClient::Process> > m_procs;
1174 
1175   Vector<SimpleCpcClient::Process> dummy;
1176   m_procs.fill(config.m_hosts.size(), dummy);
1177   for(unsigned i = 0; i<config.m_hosts.size(); i++)
1178   {
1179     if (config.m_hosts[i]->m_hostname.length() == 0)
1180       continue;
1181 
1182     Properties p;
1183     config.m_hosts[i]->m_cpcd->list_processes(m_procs[i], p);
1184   }
1185 
1186   for(unsigned i = 0; i<config.m_processes.size(); i++){
1187     atrt_process & proc = *config.m_processes[i];
1188     if(proc.m_proc.m_id != -1){
1189       Vector<SimpleCpcClient::Process> &h_procs= m_procs[proc.m_host->m_index];
1190       bool found = false;
1191       for(unsigned j = 0; j<h_procs.size(); j++){
1192 	if(proc.m_proc.m_id == h_procs[j].m_id){
1193 	  found = true;
1194 	  proc.m_proc.m_status = h_procs[j].m_status;
1195 	  break;
1196 	}
1197       }
1198       if(!found){
1199 	g_logger.error("update_status: not found");
1200 	g_logger.error("id: %d host: %s cmd: %s",
1201 		       proc.m_proc.m_id,
1202 		       proc.m_host->m_hostname.c_str(),
1203 		       proc.m_proc.m_path.c_str());
1204         for(unsigned j = 0; j<h_procs.size(); j++){
1205 	  g_logger.error("found: %d %s", h_procs[j].m_id,
1206 			 h_procs[j].m_path.c_str());
1207 	}
1208 	return false;
1209       }
1210     }
1211   }
1212   return true;
1213 }
1214 
1215 int
is_running(atrt_config & config,int types)1216 is_running(atrt_config& config, int types){
1217   int found = 0, running = 0;
1218   for(unsigned i = 0; i<config.m_processes.size(); i++){
1219     atrt_process & proc = *config.m_processes[i];
1220     if((types & proc.m_type) != 0){
1221       found++;
1222       if(proc.m_proc.m_status == "running")
1223 	running++;
1224       else {
1225         if(IF_WIN(proc.m_type & atrt_process::AP_MYSQLD, 0))  {
1226           running++;
1227         }
1228       }
1229     }
1230   }
1231 
1232   if(found == running)
1233     return 2;
1234   if(running == 0)
1235     return 0;
1236   return 1;
1237 }
1238 
1239 
1240 int
insert(const char * pair,Properties & p)1241 insert(const char * pair, Properties & p){
1242   BaseString tmp(pair);
1243 
1244   tmp.trim(" \t\n\r");
1245 
1246   Vector<BaseString> split;
1247   tmp.split(split, ":=", 2);
1248 
1249   if(split.size() != 2)
1250     return -1;
1251 
1252   p.put(split[0].trim().c_str(), split[1].trim().c_str());
1253 
1254   return 0;
1255 }
1256 
1257 bool
read_test_case(FILE * file,atrt_testcase & tc,int & line)1258 read_test_case(FILE * file, atrt_testcase& tc, int& line){
1259 
1260   Properties p;
1261   int elements = 0;
1262   char buf[1024];
1263 
1264   while(!feof(file)){
1265     if (file == stdin)
1266       printf("atrt> ");
1267     if(!fgets(buf, 1024, file))
1268       break;
1269 
1270     line++;
1271     BaseString tmp = buf;
1272 
1273     if(tmp.length() > 0 && tmp.c_str()[0] == '#')
1274       continue;
1275 
1276     if(insert(tmp.c_str(), p) != 0)
1277       break;
1278 
1279     elements++;
1280   }
1281 
1282   if(elements == 0){
1283     if(file == stdin){
1284       BaseString tmp(buf);
1285       tmp.trim(" \t\n\r");
1286       Vector<BaseString> split;
1287       tmp.split(split, " ", 2);
1288       tc.m_cmd.m_exe = split[0];
1289       if(split.size() == 2)
1290 	tc.m_cmd.m_args = split[1];
1291       else
1292 	tc.m_cmd.m_args = "";
1293       tc.m_max_time = 60000;
1294       return true;
1295     }
1296     return false;
1297   }
1298 
1299   int used_elements = 0;
1300 
1301   if(!p.get("cmd", tc.m_cmd.m_exe)){
1302     g_logger.critical("Invalid test file: cmd is missing near line: %d", line);
1303     return false;
1304   }
1305   used_elements ++;
1306 
1307   if(!p.get("args", tc.m_cmd.m_args))
1308     tc.m_cmd.m_args = "";
1309   else
1310     used_elements ++;
1311 
1312   const char * mt = 0;
1313   if(!p.get("max-time", &mt))
1314     tc.m_max_time = 60000;
1315   else
1316   {
1317     tc.m_max_time = atoi(mt);
1318     used_elements ++;
1319   }
1320 
1321   if(p.get("type", &mt))
1322   {
1323     tc.m_report= (strcmp(mt, "bench") == 0);
1324     used_elements ++;
1325   }
1326   else
1327     tc.m_report= false;
1328 
1329   if(p.get("run-all", &mt))
1330   {
1331     tc.m_run_all = (strcmp(mt, "yes") == 0);
1332     used_elements ++;
1333   }
1334   else
1335     tc.m_run_all= false;
1336 
1337   const char * str;
1338   if (p.get("mysqld", &str))
1339   {
1340     tc.m_mysqld_options.assign(str);
1341     used_elements ++;
1342   }
1343   else
1344   {
1345     tc.m_mysqld_options.assign("");
1346   }
1347 
1348   tc.m_cmd.m_cmd_type = atrt_process::AP_NDB_API;
1349   if (p.get("cmd-type", &str))
1350   {
1351     if (strcmp(str, "mysql") == 0)
1352       tc.m_cmd.m_cmd_type = atrt_process::AP_CLIENT;
1353     used_elements ++;
1354   }
1355 
1356   if (!p.get("name", &mt))
1357   {
1358     tc.m_name.assfmt("%s %s",
1359 		     tc.m_cmd.m_exe.c_str(),
1360 		     tc.m_cmd.m_args.c_str());
1361   }
1362   else
1363   {
1364     tc.m_name.assign(mt);
1365     used_elements ++;
1366   }
1367 
1368   if (used_elements != elements)
1369   {
1370     g_logger.critical("Invalid test file: unknown properties near line: %d", line);
1371     return false;
1372   }
1373 
1374   return true;
1375 }
1376 
1377 bool
setup_test_case(atrt_config & config,const atrt_testcase & tc)1378 setup_test_case(atrt_config& config, const atrt_testcase& tc){
1379 
1380   if (!remove_dir("result", true))
1381   {
1382     g_logger.critical("setup_test_case: Failed to clear result");
1383     return false;
1384   }
1385 
1386   for (unsigned i = 0; i<config.m_processes.size(); i++)
1387   {
1388     atrt_process & proc = *config.m_processes[i];
1389     if (proc.m_type == atrt_process::AP_NDB_API ||
1390         proc.m_type == atrt_process::AP_CLIENT)
1391     {
1392       proc.m_proc.m_path.assign("");
1393       proc.m_proc.m_args.assign("");
1394     }
1395   }
1396 
1397   BaseString cmd;
1398   char * p = find_bin_path(tc.m_cmd.m_exe.c_str());
1399   if (p == 0)
1400   {
1401     g_logger.critical("Failed to locate '%s'", tc.m_cmd.m_exe.c_str());
1402     return false;
1403   }
1404   cmd.assign(p);
1405   free(p);
1406 
1407   for (unsigned i = 0; i<config.m_processes.size(); i++)
1408   {
1409     atrt_process & proc = *config.m_processes[i];
1410     if (proc.m_type == tc.m_cmd.m_cmd_type &&
1411         proc.m_proc.m_path == "")
1412     {
1413       proc.m_save.m_proc = proc.m_proc;
1414       proc.m_save.m_saved = true;
1415 
1416       proc.m_proc.m_env.appfmt(" ATRT_TIMEOUT=%ld", tc.m_max_time);
1417       if (0) // valgrind
1418       {
1419         proc.m_proc.m_path = "/usr/bin/valgrind";
1420         proc.m_proc.m_args.appfmt("%s %s", cmd.c_str(),
1421                                   tc.m_cmd.m_args.c_str());
1422       }
1423       else
1424       {
1425         proc.m_proc.m_path = cmd;
1426         proc.m_proc.m_args.assign(tc.m_cmd.m_args.c_str());
1427       }
1428       if (!tc.m_run_all)
1429         break;
1430     }
1431   }
1432 
1433   if (tc.m_mysqld_options != "")
1434   {
1435     g_logger.info("restarting mysqld with extra options: %s",
1436                   tc.m_mysqld_options.c_str());
1437 
1438     /**
1439      * Apply testcase specific mysqld options
1440      */
1441     for (unsigned i = 0; i<config.m_processes.size(); i++)
1442     {
1443       atrt_process & proc = *config.m_processes[i];
1444       if (proc.m_type == atrt_process::AP_MYSQLD)
1445       {
1446         proc.m_save.m_proc = proc.m_proc;
1447         proc.m_save.m_saved = true;
1448         proc.m_proc.m_args.appfmt(" %s", tc.m_mysqld_options.c_str());
1449         if (!stop_process(proc))
1450         {
1451           return false;
1452         }
1453 
1454         if (!start_process(proc))
1455         {
1456           return false;
1457         }
1458 
1459         if (!connect_mysqld(proc))
1460         {
1461           return false;
1462         }
1463       }
1464     }
1465   }
1466 
1467   return true;
1468 }
1469 
1470 bool
gather_result(atrt_config & config,int * result)1471 gather_result(atrt_config& config, int * result){
1472   BaseString tmp = g_gather_progname;
1473 
1474   for(unsigned i = 0; i<config.m_hosts.size(); i++)
1475   {
1476     if (config.m_hosts[i]->m_hostname.length() == 0)
1477       continue;
1478 
1479     tmp.appfmt(" %s:%s/*",
1480 	       config.m_hosts[i]->m_hostname.c_str(),
1481 	       config.m_hosts[i]->m_basedir.c_str());
1482   }
1483 
1484   g_logger.debug("system(%s)", tmp.c_str());
1485   const int r1 = sh(tmp.c_str());
1486   if(r1 != 0)
1487   {
1488     g_logger.critical("Failed to gather result!");
1489     return false;
1490   }
1491 
1492   g_logger.debug("system(%s)", g_analyze_progname);
1493   const int r2 = sh(g_analyze_progname);
1494 
1495   if(r2 == -1 || r2 == (127 << 8))
1496   {
1497     g_logger.critical("Failed to analyze results");
1498     return false;
1499   }
1500 
1501   * result = r2 ;
1502   return true;
1503 }
1504 
1505 bool
setup_hosts(atrt_config & config)1506 setup_hosts(atrt_config& config){
1507   if (!remove_dir("result", true))
1508   {
1509     g_logger.critical("setup_hosts: Failed to clear result");
1510     return false;
1511   }
1512 
1513   for(unsigned i = 0; i<config.m_hosts.size(); i++)
1514   {
1515     if (config.m_hosts[i]->m_hostname.length() == 0)
1516       continue;
1517     BaseString tmp = g_setup_progname;
1518     tmp.appfmt(" %s %s/ %s/",
1519 	       config.m_hosts[i]->m_hostname.c_str(),
1520 	       g_basedir,
1521 	       config.m_hosts[i]->m_basedir.c_str());
1522 
1523     g_logger.debug("system(%s)", tmp.c_str());
1524     const int r1 = sh(tmp.c_str());
1525     if(r1 != 0){
1526       g_logger.critical("Failed to setup %s",
1527 			config.m_hosts[i]->m_hostname.c_str());
1528       return false;
1529     }
1530   }
1531   return true;
1532 }
1533 
1534 static
1535 bool
do_rsync(const char * dir,const char * dst)1536 do_rsync(const char *dir, const char *dst)
1537 {
1538   BaseString tmp = g_setup_progname;
1539   tmp.appfmt(" %s %s/ %s", dst, dir, dir);
1540 
1541   g_logger.info("rsyncing %s to %s", dir, dst);
1542   g_logger.debug("system(%s)", tmp.c_str());
1543   const int r1 = sh(tmp.c_str());
1544   if(r1 != 0)
1545   {
1546     g_logger.critical("Failed to rsync %s to %s", dir, dst);
1547     return false;
1548   }
1549 
1550   return true;
1551 }
1552 
1553 bool
deploy(int d,atrt_config & config)1554 deploy(int d, atrt_config & config)
1555 {
1556   for (unsigned i = 0; i<config.m_hosts.size(); i++)
1557   {
1558     if (config.m_hosts[i]->m_hostname.length() == 0)
1559       continue;
1560 
1561     if (d & 1)
1562     {
1563       if (!do_rsync(g_basedir, config.m_hosts[i]->m_hostname.c_str()))
1564         return false;
1565     }
1566 
1567     if (d & 2)
1568     {
1569       if (!do_rsync(g_prefix, config.m_hosts[i]->m_hostname.c_str()))
1570         return false;
1571 
1572       if (g_prefix1 &&
1573           !do_rsync(g_prefix1, config.m_hosts[i]->m_hostname.c_str()))
1574         return false;
1575     }
1576   }
1577 
1578   return true;
1579 }
1580 
1581 bool
sshx(atrt_config & config,unsigned mask)1582 sshx(atrt_config & config, unsigned mask)
1583 {
1584   for (unsigned i = 0; i<config.m_processes.size(); i++)
1585   {
1586     atrt_process & proc = *config.m_processes[i];
1587 
1588     BaseString tmp;
1589     const char * type = 0;
1590     switch(proc.m_type){
1591     case atrt_process::AP_NDB_MGMD:
1592       type = (mask & proc.m_type) ? "ndb_mgmd" : 0;
1593       break;
1594     case atrt_process::AP_NDBD:
1595       type = (mask & proc.m_type) ? "ndbd" : 0;
1596       break;
1597     case atrt_process::AP_MYSQLD:
1598       type = (mask & proc.m_type) ? "mysqld" : 0;
1599       break;
1600     case atrt_process::AP_NDB_API:
1601       type = (mask & proc.m_type) ? "ndbapi" : 0;
1602       break;
1603     case atrt_process::AP_CLIENT:
1604       type = (mask & proc.m_type) ? "client" : 0;
1605       break;
1606     default:
1607       type = "<unknown>";
1608     }
1609 
1610     if (type == 0)
1611       continue;
1612 
1613 #ifdef _WIN32
1614 #define SYS_SSH "bash '-c echo\"%s(%s) on %s\";" \
1615 	        "ssh -t %s sh %s/ssh-login.sh' &"
1616 #else
1617 #define SYS_SSH "xterm -fg black -title \"%s(%s) on %s\"" \
1618 	        " -e 'ssh -t -X %s sh %s/ssh-login.sh' &"
1619 #endif
1620 
1621     tmp.appfmt(SYS_SSH,
1622 	       type,
1623 	       proc.m_cluster->m_name.c_str(),
1624 	       proc.m_host->m_hostname.c_str(),
1625 	       proc.m_host->m_hostname.c_str(),
1626 	       proc.m_proc.m_cwd.c_str());
1627 
1628     g_logger.debug("system(%s)", tmp.c_str());
1629     const int r1 = sh(tmp.c_str());
1630     if(r1 != 0)
1631     {
1632       g_logger.critical("Failed sshx (%s)",
1633 			tmp.c_str());
1634       return false;
1635     }
1636     NdbSleep_MilliSleep(300); // To prevent xlock problem
1637   }
1638 
1639   return true;
1640 }
1641 
1642 bool
start(atrt_config & config,unsigned proc_mask)1643 start(atrt_config & config, unsigned proc_mask)
1644 {
1645   if (proc_mask & atrt_process::AP_NDB_MGMD)
1646     if(!start_processes(g_config, atrt_process::AP_NDB_MGMD))
1647       return false;
1648 
1649   if (proc_mask & atrt_process::AP_NDBD)
1650   {
1651     if(!connect_ndb_mgm(g_config)){
1652       return false;
1653     }
1654 
1655     if(!start_processes(g_config, atrt_process::AP_NDBD))
1656       return false;
1657 
1658     if(!wait_ndb(g_config, NDB_MGM_NODE_STATUS_NOT_STARTED))
1659       return false;
1660 
1661     for(Uint32 i = 0; i<3; i++)
1662       if(wait_ndb(g_config, NDB_MGM_NODE_STATUS_STARTED))
1663 	goto started;
1664     return false;
1665   }
1666 
1667 started:
1668   if(!start_processes(g_config, p_servers & proc_mask))
1669     return false;
1670 
1671   return true;
1672 }
1673 
1674 bool
reset_config(atrt_config & config)1675 reset_config(atrt_config & config)
1676 {
1677   bool changed = false;
1678   for(unsigned i = 0; i<config.m_processes.size(); i++)
1679   {
1680     atrt_process & proc = *config.m_processes[i];
1681     if (proc.m_save.m_saved)
1682     {
1683       if (proc.m_proc.m_status == "running")
1684       {
1685         if (!stop_process(proc))
1686           return false;
1687 
1688         changed = true;
1689       }
1690 
1691       proc.m_save.m_saved = false;
1692       proc.m_proc = proc.m_save.m_proc;
1693       proc.m_proc.m_id = -1;
1694     }
1695   }
1696   return changed;
1697 }
1698 
1699 static
1700 bool
find_binaries()1701 find_binaries()
1702 {
1703   g_logger.info("Locating binaries...");
1704   bool ok = true;
1705   for (int i = 0; g_binaries[i].exe != 0; i++)
1706   {
1707     const char * p = find_bin_path(g_binaries[i].exe);
1708     if (p == 0)
1709     {
1710       if (g_binaries[i].is_required)
1711       {
1712         g_logger.critical("Failed to locate '%s'", g_binaries[i].exe);
1713         ok = false;
1714       }
1715       else
1716       {
1717         g_logger.info("Failed to locate '%s'...ok", g_binaries[i].exe);
1718       }
1719     }
1720     else
1721     {
1722       * g_binaries[i].var = p;
1723     }
1724   }
1725   return ok;
1726 }
1727 
1728 template class Vector<Vector<SimpleCpcClient::Process> >;
1729 template class Vector<atrt_host*>;
1730 template class Vector<atrt_cluster*>;
1731 template class Vector<atrt_process*>;
1732 
1733 int
check_testcase_file_main(int argc,char ** argv)1734 check_testcase_file_main(int argc, char ** argv)
1735 {
1736   bool ok = true;
1737   int argi = 1;
1738   if (strcmp(argv[argi], "--check-testcase-files") == 0)
1739   {
1740     argi ++;
1741   }
1742   if (argi == argc)
1743   {
1744     ok = false;
1745     fprintf(stderr, "Error: No files to check!\n");
1746   }
1747   else for (; argi < argc; argi ++)
1748   {
1749     FILE* f = fopen(argv[argi], "r");
1750     if (f == NULL)
1751     {
1752       ok = false;
1753       perror(argv[argi]);
1754       continue;
1755     }
1756     atrt_testcase tc_dummy;
1757     int prev_line_num = 0;
1758     int line_num = 0;
1759     int ntests = 0;
1760     while (read_test_case(f, tc_dummy, line_num))
1761     {
1762       prev_line_num = line_num;
1763       ntests ++;
1764     }
1765     // If line count does not change that indicates end of file.
1766     if (line_num == prev_line_num)
1767     {
1768       printf("%s: Contains %d tests in %d lines.\n", argv[argi], ntests, line_num);
1769     }
1770     else
1771     {
1772       ok = false;
1773       fprintf(stderr, "%s: Error at line %d\n", argv[argi], line_num);
1774     }
1775     fclose(f);
1776   }
1777   return ok ? 0 : 1;
1778 }
1779 
1780 void
print_testcase_file_syntax()1781 print_testcase_file_syntax()
1782 {
1783   printf("\n"
1784          "Test cases to run are described in files passed with the\n"
1785          "--testcase-file (-f) option.\n"
1786          "\n"
1787          "A testcase is defined with some properties, one property per line,\n"
1788          "and terminated with exactly one empty line.  No other empty lines\n"
1789          "are allowed in the file.  Lines starting with # are comments and\n"
1790          "are ignored, note they are not counted as empty lines.\n"
1791          "\n"
1792          "The properties are:\n"
1793          "cmd      - Names the test executable.  The only mandatory property.\n"
1794          "args     - The arguments to test executable.\n"
1795          "max-time - Maximum run time for test in seconds (default 60000).\n"
1796          "type     - Declare the type of the test.  The only recognized value\n"
1797          "           is 'bench' which implies that results are stored also for\n"
1798          "           successful tests.  Normally if this option is not used\n"
1799          "           only results from failed tests will be stored.\n"
1800          "run-all  - If 'yes' atrt will start the same command for each defined\n"
1801          "           api/mysqld, normally it only starts one instance.\n"
1802          "mysqld   - Arguments that atrt will use when starting mysqld.\n"
1803          "cmd-type - If 'mysql' change test process type from ndbapi to client.\n"
1804          "name     - Change name of test.  Default is given by cmd and args.\n"
1805          "\n"
1806          "Example:\n"
1807          "# BASIC FUNCTIONALITY\n"
1808          "max-time: 500\n"
1809          "cmd: testBasic\n"
1810          "args: -n PkRead\n"
1811          "\n"
1812          "# 4k record DD\n"
1813          "max-time: 600\n"
1814          "cmd: flexAsynch\n"
1815          "args: -dd -temp -con 2 -t 8 -r 2 -p 64 -ndbrecord -a 25 -s 40\n"
1816          "type: bench\n"
1817          "\n"
1818          "# sql\n"
1819          "max-time: 600\n"
1820          "cmd: ndb-sql-perf.sh\n"
1821          "args: ndb-sql-perf-select.sh t1 1 64\n"
1822          "mysqld: --ndb-cluster-connection-pool=1\n"
1823          "type: bench\n"
1824          "cmd-type: mysql\n"
1825          "\n"
1826   );
1827 }
1828