1 /*
2 Copyright (c) 2008, 2021, Oracle and/or its affiliates.
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", §ion));
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 (unsigned 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 (unsigned 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 (unsigned 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