1 /*
2   Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License, version 2.0,
7    as published by the Free Software Foundation.
8 
9    This program is also distributed with certain software (including
10    but not limited to OpenSSL) that is licensed under separate terms,
11    as designated in a particular file or component or in included license
12    documentation.  The authors of MySQL hereby grant you an additional
13    permission to link the program and your derivative works with the
14    separately licensed software that they have included with MySQL.
15 
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License, version 2.0, for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 */
25 
26 #include <ndb_global.h>
27 #include "InitConfigFileParser.hpp"
28 #include "ConfigInfo.hpp"
29 #include "Config.hpp"
30 #include <portlib/NdbDir.hpp>
31 
32 #define CHECK(x) \
33   if (!(x)) {\
34     fprintf(stderr, "testConfig: '"#x"' failed on line %d\n", __LINE__); \
35     exit(1); \
36   }
37 
38 
39 static const ConfigInfo g_info;
40 
41 /*
42   Create a small config.ini with the given parameter and run
43   it through InitConfigFileParser
44  */
45 bool
check_param(const ConfigInfo::ParamInfo & param)46 check_param(const ConfigInfo::ParamInfo & param)
47 {
48   FILE* config_file= tmpfile();
49   CHECK(config_file);
50 
51   const char* section= g_info.nameToAlias(param._section);
52   if (section == NULL)
53     section= param._section;
54 
55   if (param._type == ConfigInfo::CI_SECTION)
56   {
57     fclose(config_file);
58     return true;
59   }
60 
61   if(param._default == MANDATORY)
62   {
63     // Mandatory parameter
64     fclose(config_file);
65     return true;
66   }
67   else
68   {
69     fprintf(config_file, "[%s]\n", section);
70     fprintf(config_file, "%s=%s\n", param._fname,
71             param._default ? param._default : "some value");
72   }
73 
74   // Fill in lines needed for a minimal config
75   if (strcmp(section, "NDBD") != 0)
76     fprintf(config_file, "[ndbd]\n");
77   if (strcmp(param._fname, "NoOfReplicas") != 0)
78     fprintf(config_file, "NoOfReplicas=1\n");
79 
80   if (strcmp(section, "NDB_MGMD") != 0)
81     fprintf(config_file, "[ndb_mgmd]\n");
82   if (strcmp(param._fname, "Hostname") != 0)
83     fprintf(config_file, "HostName=localhost\n");
84 
85   if (strcmp(section, "MYSQLD") != 0)
86     fprintf(config_file, "[mysqld]\n");
87 
88   rewind(config_file);
89 
90   // Run the config file through InitConfigFileParser
91   InitConfigFileParser parser;
92   Config* conf = parser.parseConfig(config_file);
93   fclose(config_file);
94 
95   if (conf == NULL)
96     return false;
97   delete conf;
98   return true;
99 }
100 
101 
102 bool
check_params(void)103 check_params(void)
104 {
105   bool ok= true;
106   for (int j=0; j<g_info.m_NoOfParams; j++) {
107     const ConfigInfo::ParamInfo & param= g_info.m_ParamInfo[j];
108     printf("Checking %s...\n", param._fname);
109     if (!check_param(param))
110     {
111       ok= false;
112     }
113   }
114 
115   return true; // Ignore "ok" for now, just checking it doesn't crash
116 }
117 
118 
119 
120 Config*
create_config(const char * first,...)121 create_config(const char* first, ...)
122 {
123   va_list args;
124 
125   FILE* config_file= tmpfile();
126   CHECK(config_file);
127 
128   va_start(args, first);
129   const char* str= first;
130   do
131     fprintf(config_file, "%s\n", str);
132   while((str= va_arg(args, const char*)) != NULL);
133   va_end(args);
134 
135 #if 0
136   rewind(config_file);
137 
138   char buf[100];
139   while(fgets(buf, sizeof(buf), config_file))
140     ndbout_c(buf);
141 #endif
142 
143   rewind(config_file);
144 
145   InitConfigFileParser parser;
146   Config* conf = parser.parseConfig(config_file);
147   fclose(config_file);
148 
149   return conf;
150 }
151 
152 // Global variable for my_getopt
153 extern "C" const char* my_defaults_file;
154 
155 static
156 unsigned
ndb_procid()157 ndb_procid()
158 {
159 #ifdef _WIN32
160   return (unsigned)GetCurrentProcessId();
161 #else
162   return (unsigned)getpid();
163 #endif
164 }
165 
166 Config*
create_mycnf(const char * first,...)167 create_mycnf(const char* first, ...)
168 {
169   va_list args;
170 
171   NdbDir::Temp tempdir;
172   BaseString mycnf_file;
173   mycnf_file.assfmt("%s%stest_my.%u.cnf",
174                     tempdir.path(), DIR_SEPARATOR, ndb_procid());
175 
176   FILE* config_file= fopen(mycnf_file.c_str(), "w+");
177   CHECK(config_file);
178 
179   va_start(args, first);
180   const char* str= first;
181   do
182     fprintf(config_file, "%s\n", str);
183   while((str= va_arg(args, const char*)) != NULL);
184   va_end(args);
185 
186 #if 0
187   rewind(config_file);
188 
189   char buf[100];
190   while(fgets(buf, sizeof(buf), config_file))
191     printf("%s", buf);
192 #endif
193 
194   fflush(config_file);
195   rewind(config_file);
196 
197   // Trick handle_options to read from the temp file
198   const char* save_defaults_file = my_defaults_file;
199   my_defaults_file = mycnf_file.c_str();
200 
201   InitConfigFileParser parser;
202   Config* conf = parser.parse_mycnf();
203 
204   // Restore the global variable
205   my_defaults_file = save_defaults_file;
206 
207   fclose(config_file);
208 
209   // Remove file
210   unlink(mycnf_file.c_str());
211 
212   return conf;
213 }
214 
215 
216 
217 
218 void
diff_config(void)219 diff_config(void)
220 {
221   Config* c1=
222     create_config("[ndbd]", "NoOfReplicas=1",
223                   "[ndb_mgmd]", "HostName=localhost",
224                   "[mysqld]", NULL);
225   CHECK(c1);
226   Config* c2=
227     create_config("[ndbd]", "NoOfReplicas=1",
228                   "[ndb_mgmd]", "HostName=localhost",
229                   "[mysqld]", "[mysqld]", NULL);
230   CHECK(c2);
231 
232   CHECK(c1->equal(c1));
233 
234   CHECK(!c1->equal(c2));
235   CHECK(!c2->equal(c1));
236   CHECK(!c2->illegal_change(c1));
237   CHECK(!c1->illegal_change(c2));
238 
239   ndbout_c("==================");
240   ndbout_c("c1->print_diff(c2)");
241   c1->print_diff(c2);
242   ndbout_c("==================");
243   ndbout_c("c2->print_diff(c1)");
244   c2->print_diff(c1);
245   ndbout_c("==================");
246 
247   {
248 
249 
250     // BUG#47036 Reload of config shows only diff of last changed parameter
251     // - check that diff of c1 and c3 shows 2 diffs
252     Config* c1_bug47306=
253       create_config("[ndbd]", "NoOfReplicas=1",
254                     "DataMemory=100M", "IndexMemory=100M",
255                     "[ndb_mgmd]", "HostName=localhost",
256                     "[mysqld]", NULL);
257     CHECK(c1_bug47306);
258 
259     ndbout_c("c1->print_diff(c1_bug47306)");
260     c1->print_diff(c1_bug47306);
261 
262     Properties diff_list;
263     unsigned exclude[]= {CFG_SECTION_SYSTEM, 0};
264     c1->diff(c1_bug47306, diff_list, exclude);
265 
266     // open section for ndbd with NodeId=1
267     const Properties* section;
268     CHECK(diff_list.get("NodeId=1", &section));
269 
270     // Count the number of diffs for ndbd 1
271     const char* name;
272     int count= 0, found = 0;
273     Properties::Iterator prop_it(section);
274     while ((name = prop_it.next())){
275       if (strcmp(name, "IndexMemory") == 0)
276         found++;
277       if (strcmp(name, "DataMemory") == 0)
278         found++;
279       count++;
280     }
281     CHECK(found == 2 &&
282           count == found + 2); // Overhead == 2
283     ndbout_c("==================");
284 
285     delete c1_bug47306;
286   }
287 
288   delete c1;
289   delete c2;
290 }
291 
292 
293 void
print_restart_info(void)294 print_restart_info(void)
295 {
296   Vector<const char*> initial_node;
297   Vector<const char*> system;
298   Vector<const char*> initial_system;
299 
300   for (int i = 0; i < g_info.m_NoOfParams; i++) {
301     const ConfigInfo::ParamInfo & param = g_info.m_ParamInfo[i];
302     if ((param._flags & ConfigInfo::CI_RESTART_INITIAL) &&
303         (param._flags & ConfigInfo::CI_RESTART_SYSTEM))
304       initial_system.push_back(param._fname);
305     else if (param._flags & (ConfigInfo::CI_RESTART_SYSTEM))
306       system.push_back(param._fname);
307     else if (param._flags & (ConfigInfo::CI_RESTART_INITIAL))
308       initial_node.push_back(param._fname);
309   }
310 
311   fprintf(stderr, "*** initial node restart ***\n");
312   for (size_t i = 0; i < initial_node.size(); i++) {
313     fprintf(stderr, "%s\n", initial_node[i]);
314   }
315   fprintf(stderr, "\n");
316 
317   fprintf(stderr, "*** system restart ***\n");
318   for (size_t i = 0; i < system.size(); i++) {
319     fprintf(stderr, "%s\n", system[i]);
320   }
321   fprintf(stderr, "\n");
322 
323   fprintf(stderr, "*** initial system restart ***\n");
324   for (size_t i = 0; i < initial_system.size(); i++) {
325     fprintf(stderr, "%s\n", initial_system[i]);
326   }
327   fprintf(stderr, "\n");
328 }
329 
330 
331 static void
checksum_config(void)332 checksum_config(void)
333 {
334   Config* c1=
335     create_config("[ndbd]", "NoOfReplicas=1",
336                   "[ndb_mgmd]", "HostName=localhost",
337                   "[mysqld]", NULL);
338   CHECK(c1);
339   Config* c2=
340     create_config("[ndbd]", "NoOfReplicas=1",
341                   "[ndb_mgmd]", "HostName=localhost",
342                   "[mysqld]", "[mysqld]", NULL);
343   CHECK(c2);
344 
345   ndbout_c("== checksum tests ==");
346   Uint32 c1_check = c1->checksum();
347   Uint32 c2_check = c2->checksum();
348   ndbout_c("c1->checksum(): 0x%x", c1_check);
349   ndbout_c("c2->checksum(): 0x%x", c2_check);
350    // Different config should not have same checksum
351   CHECK(c1_check != c2_check);
352 
353   // Same config should have same checksum
354   CHECK(c1_check == c1->checksum());
355 
356   // Copied config should have same checksum
357   Config c1_copy(c1);
358   CHECK(c1_check == c1_copy.checksum());
359 
360   ndbout_c("==================");
361 
362   delete c1;
363   delete c2;
364 }
365 
366 static void
test_param_values(void)367 test_param_values(void)
368 {
369   struct test {
370     const char* param;
371     bool result;
372   } tests [] = {
373     // CI_ENUM
374     { "Arbitration=Disabled", true },
375     { "Arbitration=Invalid", false },
376     { "Arbitration=", false },
377     // CI_BITMASK
378     { "LockExecuteThreadToCPU=0", true },
379     { "LockExecuteThreadToCPU=1", true },
380     { "LockExecuteThreadToCPU=65535", true },
381     { "LockExecuteThreadToCPU=0-65535", true },
382     { "LockExecuteThreadToCPU=0-1,65534-65535", true },
383     { "LockExecuteThreadToCPU=17-256", true },
384     { "LockExecuteThreadToCPU=1-2,36-37,17-256,11-12,1-2", true },
385     { "LockExecuteThreadToCPU=", false }, // Zero size value not allowed
386     { "LockExecuteThreadToCPU=1-", false },
387     { "LockExecuteThreadToCPU=1--", false },
388     { "LockExecuteThreadToCPU=1-2,34-", false },
389     { "LockExecuteThreadToCPU=x", false },
390     { "LockExecuteThreadToCPU=x-1", false },
391     { "LockExecuteThreadToCPU=x-x", false },
392     { 0, false }
393   };
394 
395   for (struct test* t = tests; t->param; t++)
396   {
397     ndbout_c("testing %s", t->param);
398     {
399       const Config* c =
400         create_config("[ndbd]", "NoOfReplicas=1",
401                       t->param,
402                       "[ndb_mgmd]", "HostName=localhost",
403                       "[mysqld]", NULL);
404       if (t->result)
405       {
406         CHECK(c);
407         delete c;
408       }
409       else
410       {
411         CHECK(c == NULL);
412       }
413     }
414     {
415       const Config* c =
416         create_mycnf("[cluster_config]",
417                      "ndb_mgmd=localhost",
418                      "ndbd=localhost,localhost",
419                      "ndbapi=localhost",
420                      "NoOfReplicas=1",
421                      t->param,
422                      NULL);
423       if (t->result)
424       {
425         CHECK(c);
426         delete c;
427       }
428       else
429       {
430         CHECK(c == NULL);
431       }
432     }
433   }
434 
435 }
436 
437 static void
test_hostname_mycnf(void)438 test_hostname_mycnf(void)
439 {
440   // Check the special rule for my.cnf that says
441   // the two hostname specs must match
442   {
443     // Valid config, ndbd=localhost, matches HostName=localhost
444     const Config* c =
445       create_mycnf("[cluster_config]",
446                    "ndb_mgmd=localhost",
447                    "ndbd=localhost,localhost",
448                    "ndbapi=localhost",
449                    "NoOfReplicas=1",
450                    "[cluster_config.ndbd.1]",
451                    "HostName=localhost",
452                    NULL);
453     CHECK(c);
454     delete c;
455   }
456 
457   {
458     // Invalid config, ndbd=localhost, does not match HostName=host1
459     const Config* c =
460       create_mycnf("[cluster_config]",
461                    "ndb_mgmd=localhost",
462                    "ndbd=localhost,localhost",
463                    "ndbapi=localhost",
464                    "NoOfReplicas=1",
465                    "[cluster_config.ndbd.1]",
466                    "HostName=host1",
467                    NULL);
468     CHECK(c == NULL);
469   }
470 }
471 
472 #include <NdbTap.hpp>
473 
474 #include <EventLogger.hpp>
475 extern EventLogger* g_eventLogger;
476 
TAPTEST(MgmConfig)477 TAPTEST(MgmConfig)
478 {
479   ndb_init();
480   g_eventLogger->createConsoleHandler();
481   diff_config();
482   CHECK(check_params());
483   checksum_config();
484   test_param_values();
485   test_hostname_mycnf();
486 #if 0
487   print_restart_info();
488 #endif
489   ndb_end(0);
490   return 1; // OK
491 }
492