/* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "ConfigFactory.hpp" #include #include #include #include #include static const char * exe_valgrind = 0; static const char * arg_valgrind = 0; static bool file_exists(const char* path, Uint32 timeout = 1) { g_info << "File '" << path << "' "; /** * ndb_mgmd does currently not fsync the directory * after committing config-bin, * which means that it can be on disk, wo/ being visible * remedy this by retrying some */ for (Uint32 i = 0; i < 10 * timeout; i++) { if (access(path, F_OK) == 0) { g_info << "exists" << endl; return true; } if (i == 0) { g_info << "does not exist, retrying..."; } NdbSleep_MilliSleep(100); } g_info << "does not exist" << endl; return false; } // Util function that concatenate strings to form a path static BaseString path(const char* first, ...) { BaseString path; path.assign(first); const char* str; va_list args; va_start(args, first); while ((str = va_arg(args, const char*)) != NULL) { path.appfmt("%s%s", DIR_SEPARATOR, str); } va_end(args); return path; } class Mgmd { NdbProcess* m_proc; int m_nodeid; BaseString m_name; BaseString m_exe; NdbMgmd m_mgmd_client; Mgmd(const Mgmd& other); // Not implemented public: Mgmd(int nodeid) : m_proc(NULL), m_nodeid(nodeid) { m_name.assfmt("ndb_mgmd_%d", nodeid); NDBT_find_ndb_mgmd(m_exe); } ~Mgmd() { if (m_proc) { //stop the proces stop(); } } const char* name(void) const { return m_name.c_str(); } const char* exe(void) const { return m_exe.c_str(); } bool start(const char* working_dir, NdbProcess::Args& args) { g_info << "Starting " << name() << " "; for (unsigned i = 0; i < args.args().size(); i++) g_info << args.args()[i].c_str() << " "; g_info << endl; if (exe_valgrind == 0) { m_proc = NdbProcess::create(name(), exe(), working_dir, args); } else { NdbProcess::Args copy; if (arg_valgrind) { copy.add(arg_valgrind); } copy.add(exe()); copy.add(args); m_proc = NdbProcess::create(name(), BaseString(exe_valgrind), working_dir, copy); } return (m_proc != NULL); } bool start_from_config_ini(const char* working_dir, const char* first_extra_arg = NULL, ...) { NdbProcess::Args args; args.add("--no-defaults"); args.add("--configdir=."); args.add("-f config.ini"); args.add("--ndb-nodeid=", m_nodeid); args.add("--nodaemon"); args.add("--log-name=", name()); args.add("--verbose"); if (first_extra_arg) { // Append any extra args va_list extra_args; const char* str = first_extra_arg; va_start(extra_args, first_extra_arg); do { args.add(str); } while ((str = va_arg(extra_args, const char*)) != NULL); va_end(extra_args); } return start(working_dir, args); } bool start(const char* working_dir, const char* first_extra_arg = NULL, ...) { NdbProcess::Args args; args.add("--no-defaults"); args.add("--configdir=."); args.add("--ndb-nodeid=", m_nodeid); args.add("--nodaemon"); args.add("--log-name=", name()); args.add("--verbose"); if (first_extra_arg) { // Append any extra args va_list extra_args; const char* str = first_extra_arg; va_start(extra_args, first_extra_arg); do { args.add(str); } while ((str = va_arg(extra_args, const char*)) != NULL); va_end(extra_args); } return start(working_dir, args); } bool stop(void) { g_info << "Stopping " << name() << endl; // Diconnect and close our "builtin" client m_mgmd_client.close(); if (m_proc == 0 || !m_proc->stop()) { fprintf(stderr, "Failed to stop process %s\n", name()); return false; // Can't kill with -9 -> fatal error } int ret; if (!m_proc->wait(ret, 300)) { fprintf(stderr, "Failed to wait for process %s\n", name()); return false; // Can't wait after kill with -9 -> fatal error } if (ret != 9) { fprintf(stderr, "stop ret: %u\n", ret); return false; // Can't wait after kill with -9 -> fatal error } delete m_proc; m_proc = 0; return true; } bool wait(int& ret, int timeout = 300) { g_info << "Waiting for " << name() << endl; if (m_proc == 0 || !m_proc->wait(ret, timeout)) { fprintf(stderr, "Failed to wait for process %s\n", name()); return false; } delete m_proc; m_proc = 0; return true; } const BaseString connectstring(const Properties& config) { const char* hostname; require(get_section_string(config, m_name.c_str(), "HostName", &hostname)); Uint32 port; require(get_section_uint32(config, m_name.c_str(), "PortNumber", &port)); BaseString constr; constr.assfmt("%s:%d", hostname, port); return constr; } bool connect(const Properties& config, int num_retries = 60, int retry_delay_in_seconds = 1) { BaseString constr = connectstring(config); g_info << "Connecting to " << name() << " @ " << constr.c_str() << endl; return m_mgmd_client.connect(constr.c_str(), num_retries, retry_delay_in_seconds); } bool wait_confirmed_config(int timeout = 30) { if (!m_mgmd_client.is_connected()) { g_err << "wait_confirmed_config: not connected!" << endl; return false; } int retries = 0; Config conf; while (!m_mgmd_client.get_config(conf)) { retries++; if (retries == timeout * 10) { g_err << "wait_confirmed_config: Failed to get config within " << timeout << " seconds" << endl; return false; } g_err << "Failed to get config, sleeping" << endl; NdbSleep_MilliSleep(100); } g_info << "wait_confirmed_config: ok" << endl; return true; } NdbMgmHandle handle() { return m_mgmd_client.handle(); } private: bool get_section_string(const Properties& config, const char* section_name, const char* key, const char** value) const { const Properties* section; if (!config.get(section_name, §ion)) return false; if (!section->get(key, value)) return false; return true; } bool get_section_uint32(const Properties& config, const char* section_name, const char* key, Uint32* value) const { const Properties* section; if (!config.get(section_name, §ion)) return false; if (!section->get(key, value)) return false; return true; } }; class MgmdProcessList : public Vector { public: ~MgmdProcessList() { // Delete and thus stop the mgmd(s) for (unsigned i = 0; i < size(); i++) { Mgmd* mgmd = this->operator[](i); delete mgmd; } // delete this->[i]; clear(); } }; #define CHECK(x) \ if (!(x)) { \ fprintf(stderr, "CHECK(" #x ") failed at line: %d\n", \ __LINE__); \ return NDBT_FAILED; \ } int runTestBasic2Mgm(NDBT_Context* ctx, NDBT_Step* step) { NDBT_Workingdir wd("test_mgmd"); // temporary working directory // Create config.ini Properties config = ConfigFactory::create(2); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); // Start ndb_mgmd(s) MgmdProcessList mgmds; for (int i = 1; i <= 2; i++) { Mgmd* mgmd = new Mgmd(i); mgmds.push_back(mgmd); CHECK(mgmd->start_from_config_ini(wd.path())); } // Connect the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->connect(config)); // wait for confirmed config for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->wait_confirmed_config()); // Check binary config files created CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); // Stop the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->stop()); // Start up the mgmd(s) again from config.bin for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->start_from_config_ini(wd.path())); // Connect the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->connect(config)); // check ndb_X_config.bin.1 still exists but not ndb_X_config.bin.2 CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); CHECK(!file_exists(path(wd.path(), "ndb_1_config.bin.2", NULL).c_str())); CHECK(!file_exists(path(wd.path(), "ndb_2_config.bin.2", NULL).c_str())); return NDBT_OK; } int runTestBug45495(NDBT_Context* ctx, NDBT_Step* step) { NDBT_Workingdir wd("test_mgmd"); // temporary working directory g_err << "** Create config.ini" << endl; Properties config = ConfigFactory::create(2); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); // Start ndb_mgmd(s) MgmdProcessList mgmds; for (int i = 1; i <= 2; i++) { Mgmd* mgmd = new Mgmd(i); mgmds.push_back(mgmd); CHECK(mgmd->start_from_config_ini(wd.path())); } // Connect the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->connect(config)); // wait for confirmed config for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->wait_confirmed_config()); // Check binary config files created CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); g_err << "** Restart one ndb_mgmd at a time --reload + --initial" << endl; for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->stop()); CHECK(mgmds[i]->start_from_config_ini(wd.path(), "--reload", "--initial", NULL)); CHECK(mgmds[i]->connect(config)); CHECK(mgmds[i]->wait_confirmed_config()); // check ndb_X_config.bin.1 still exists but not ndb_X_config.bin.2 CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); CHECK(!file_exists(path(wd.path(), "ndb_1_config.bin.2", NULL).c_str())); CHECK(!file_exists(path(wd.path(), "ndb_2_config.bin.2", NULL).c_str())); } g_err << "** Restart one ndb_mgmd at a time --initial" << endl; for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->stop()); CHECK(mgmds[i]->start_from_config_ini(wd.path(), "--initial", NULL)); CHECK(mgmds[i]->connect(config)); CHECK(mgmds[i]->wait_confirmed_config()); // check ndb_X_config.bin.1 still exists but not ndb_X_config.bin.2 CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); CHECK(!file_exists(path(wd.path(), "ndb_1_config.bin.2", NULL).c_str())); CHECK(!file_exists(path(wd.path(), "ndb_2_config.bin.2", NULL).c_str())); } g_err << "** Create config2.ini" << endl; CHECK(ConfigFactory::put(config, "ndb_mgmd", 1, "ArbitrationDelay", 100)); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config2.ini", NULL).c_str())); g_err << "** Restart one ndb_mgmd at a time --initial should not work" << endl; for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->stop()); // Start from config2.ini CHECK(mgmds[i]->start_from_config_ini(wd.path(), "-f config2.ini", "--initial", NULL)); // Wait for mgmd to exit and check return status int ret; CHECK(mgmds[i]->wait(ret)); CHECK(ret == 1); // check config files exist only for the still running mgmd(s) for (unsigned j = 0; j < mgmds.size(); j++) { BaseString tmp; tmp.assfmt("ndb_%d_config.bin.1", j+1); CHECK(file_exists(path(wd.path(), tmp.c_str(), NULL).c_str()) == (j != i)); } // Start from config.ini again CHECK(mgmds[i]->start_from_config_ini(wd.path(), "--initial", "--reload", NULL)); CHECK(mgmds[i]->connect(config)); CHECK(mgmds[i]->wait_confirmed_config()); } g_err << "** Reload from config2.ini" << endl; for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->stop()); // Start from config2.ini CHECK(mgmds[i]->start_from_config_ini(wd.path(), "-f config2.ini", "--reload", NULL)); CHECK(mgmds[i]->connect(config)); CHECK(mgmds[i]->wait_confirmed_config()); } CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); Uint32 timeout = 30; CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.2", NULL).c_str(), timeout)); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.2", NULL).c_str(), timeout)); g_err << "** Reload mgmd initial(from generation=2)" << endl; for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->stop()); CHECK(mgmds[i]->start_from_config_ini(wd.path(), "-f config2.ini", "--reload", "--initial", NULL)); CHECK(mgmds[i]->connect(config)); CHECK(mgmds[i]->wait_confirmed_config()); // check config files exist for (unsigned j = 0; j < mgmds.size(); j++) { BaseString tmp; tmp.assfmt("ndb_%d_config.bin.1", j+1); CHECK(file_exists(path(wd.path(), tmp.c_str(), NULL).c_str()) == (i < j)); tmp.assfmt("ndb_%d_config.bin.2", j+1); CHECK(file_exists(path(wd.path(), tmp.c_str(), NULL).c_str(), timeout)); } } return NDBT_OK; } int runTestBug42015(NDBT_Context* ctx, NDBT_Step* step) { NDBT_Workingdir wd("test_mgmd"); // temporary working directory g_err << "** Create config.ini" << endl; Properties config = ConfigFactory::create(2); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); MgmdProcessList mgmds; // Start ndb_mgmd 1 from config.ini Mgmd* mgmd = new Mgmd(1); mgmds.push_back(mgmd); CHECK(mgmd->start_from_config_ini(wd.path())); // Start ndb_mgmd 2 by fetching from first Mgmd* mgmd2 = new Mgmd(2); mgmds.push_back(mgmd2); CHECK(mgmd2->start(wd.path(), "--ndb-connectstring", mgmd->connectstring(config).c_str(), NULL)); // Connect the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->connect(config)); // wait for confirmed config for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->wait_confirmed_config()); // Check binary config files created CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); return NDBT_OK; } /* Test for bug 53008: --skip-config-cache */ int runTestNoConfigCache(NDBT_Context* ctx, NDBT_Step* step) { NDBT_Workingdir wd("test_mgmd"); // temporary working directory g_err << "** Create config.ini" << endl; Properties config = ConfigFactory::create(); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); // Start ndb_mgmd from config.ini Mgmd* mgmd = new Mgmd(1); CHECK(mgmd->start_from_config_ini(wd.path(), "--skip-config-cache", NULL)); // Connect the ndb_mgmd(s) CHECK(mgmd->connect(config)); // wait for confirmed config CHECK(mgmd->wait_confirmed_config()); // Check binary config files *not* created bool bin_conf_file = file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str()); CHECK(bin_conf_file == false); mgmd->stop(); return NDBT_OK; } /* Test for BUG#13428853 */ int runTestNoConfigCache_DontCreateConfigDir(NDBT_Context* ctx, NDBT_Step* step) { NDBT_Workingdir wd("test_mgmd"); // temporary working directory g_err << "** Create config.ini" << endl; Properties config = ConfigFactory::create(); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); g_err << "Test no configdir is created with --skip-config-cache" << endl; Mgmd* mgmd = new Mgmd(1); CHECK(mgmd->start_from_config_ini(wd.path(), "--skip-config-cache", "--config-dir=dir37", NULL)); // Connect the ndb_mgmd(s) CHECK(mgmd->connect(config)); // wait for confirmed config CHECK(mgmd->wait_confirmed_config()); // Check configdir not created CHECK(!file_exists(path(wd.path(), "dir37", NULL).c_str())); mgmd->stop(); g_err << "Also test --initial --skip-config-cache" << endl; // Also test starting ndb_mgmd --initial --skip-config-cache CHECK(mgmd->start_from_config_ini(wd.path(), "--skip-config-cache", "--initial", "--config-dir=dir37", NULL)); // Connect the ndb_mgmd(s) CHECK(mgmd->connect(config)); // wait for confirmed config CHECK(mgmd->wait_confirmed_config()); // Check configdir not created CHECK(!file_exists(path(wd.path(), "dir37", NULL).c_str())); mgmd->stop(); return NDBT_OK; } int runTestNoConfigCache_Fetch(NDBT_Context* ctx, NDBT_Step* step) { NDBT_Workingdir wd("test_mgmd"); // temporary working directory Properties config = ConfigFactory::create(2); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); MgmdProcessList mgmds; // Start ndb_mgmd 1 from config.ini without config cache Mgmd* mgmd = new Mgmd(1); mgmds.push_back(mgmd); CHECK(mgmd->start_from_config_ini(wd.path(), "--skip-config-cache", NULL)); // Start ndb_mgmd 2 without config cache and by fetching from first Mgmd* mgmd2 = new Mgmd(2); mgmds.push_back(mgmd2); CHECK(mgmd2->start(wd.path(), "--ndb-connectstring", mgmd->connectstring(config).c_str(), "--skip-config-cache", NULL)); // Connect the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->connect(config)); // wait for confirmed config for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->wait_confirmed_config()); return NDBT_OK; } int runTestNowaitNodes(NDBT_Context* ctx, NDBT_Step* step) { MgmdProcessList mgmds; NDBT_Workingdir wd("test_mgmd"); // temporary working directory // Create config.ini unsigned nodeids[] = { 1, 2 }; Properties config = ConfigFactory::create(2, 1, 1, nodeids); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); BaseString binfile[2]; binfile[0].assfmt("ndb_%u_config.bin.1", nodeids[0]); binfile[1].assfmt("ndb_%u_config.bin.1", nodeids[1]); // Start first ndb_mgmd Mgmd* mgmd1 = new Mgmd(nodeids[0]); { mgmds.push_back(mgmd1); BaseString arg; arg.assfmt("--nowait-nodes=%u", nodeids[1]); CHECK(mgmd1->start_from_config_ini(wd.path(), "--initial", arg.c_str(), NULL)); // Connect the ndb_mgmd CHECK(mgmd1->connect(config)); // wait for confirmed config CHECK(mgmd1->wait_confirmed_config()); // Check binary config file created CHECK(file_exists(path(wd.path(), binfile[0].c_str(), NULL).c_str())); } // Start second ndb_mgmd { Mgmd* mgmd2 = new Mgmd(nodeids[1]); mgmds.push_back(mgmd2); CHECK(mgmd2->start_from_config_ini(wd.path(), "--initial", NULL)); // Connect the ndb_mgmd CHECK(mgmd2->connect(config)); // wait for confirmed config CHECK(mgmd2->wait_confirmed_config()); // Check binary config file created CHECK(file_exists(path(wd.path(), binfile[1].c_str(), NULL).c_str())); } // Create new config.ini g_err << "** Create config2.ini" << endl; CHECK(ConfigFactory::put(config, "ndb_mgmd", nodeids[0], "ArbitrationDelay", 100)); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config2.ini", NULL).c_str())); g_err << "** Reload second mgmd from config2.ini" << endl; { Mgmd* mgmd2 = mgmds[1]; CHECK(mgmd2->stop()); // Start from config2.ini CHECK(mgmd2->start_from_config_ini(wd.path(), "-f config2.ini", "--reload", NULL)); CHECK(mgmd2->connect(config)); CHECK(mgmd1->wait_confirmed_config()); CHECK(mgmd2->wait_confirmed_config()); CHECK(file_exists(path(wd.path(), binfile[0].c_str(), NULL).c_str())); CHECK(file_exists(path(wd.path(), binfile[1].c_str(), NULL).c_str())); // Both ndb_mgmd(s) should have reloaded and new binary config exist binfile[0].assfmt("ndb_%u_config.bin.2", nodeids[0]); binfile[1].assfmt("ndb_%u_config.bin.2", nodeids[1]); CHECK(file_exists(path(wd.path(), binfile[0].c_str(), NULL).c_str())); CHECK(file_exists(path(wd.path(), binfile[1].c_str(), NULL).c_str())); } // Stop the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->stop()); return NDBT_OK; } int runTestNowaitNodes2(NDBT_Context* ctx, NDBT_Step* step) { int ret; NDBT_Workingdir wd("test_mgmd"); // temporary working directory // Create config.ini Properties config = ConfigFactory::create(2); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); g_err << "** Start mgmd1 from config.ini" << endl; MgmdProcessList mgmds; Mgmd* mgmd1 = new Mgmd(1); mgmds.push_back(mgmd1); CHECK(mgmd1->start_from_config_ini(wd.path(), "--initial", "--nowait-nodes=1-255", NULL)); CHECK(mgmd1->connect(config)); CHECK(mgmd1->wait_confirmed_config()); // check config files exist CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); g_err << "** Create config2.ini" << endl; CHECK(ConfigFactory::put(config, "ndb_mgmd", 1, "ArbitrationDelay", 100)); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config2.ini", NULL).c_str())); g_err << "** Start mgmd2 from config2.ini" << endl; Mgmd* mgmd2 = new Mgmd(2); mgmds.push_back(mgmd2); CHECK(mgmd2->start_from_config_ini(wd.path(), "-f config2.ini", "--initial", "--nowait-nodes=1-255", NULL)); CHECK(mgmd2->wait(ret)); CHECK(ret == 1); CHECK(mgmd1->stop()); g_err << "** Start mgmd2 again from config2.ini" << endl; CHECK(mgmd2->start_from_config_ini(wd.path(), "-f config2.ini", "--initial", "--nowait-nodes=1-255", NULL)); CHECK(mgmd2->connect(config)); CHECK(mgmd2->wait_confirmed_config()); // check config files exist CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); g_err << "** Start mgmd1 from config.ini, mgmd2 should shutdown" << endl; CHECK(mgmd1->start_from_config_ini(wd.path(), "--initial", "--nowait-nodes=1-255", NULL)); CHECK(mgmd2->wait(ret)); CHECK(ret == 1); CHECK(mgmd1->stop()); return NDBT_OK; } int runBug56844(NDBT_Context* ctx, NDBT_Step* step) { NDBT_Workingdir wd("test_mgmd"); // temporary working directory g_err << "** Create config.ini" << endl; Properties config = ConfigFactory::create(2); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); // Start ndb_mgmd(s) MgmdProcessList mgmds; for (int i = 1; i <= 2; i++) { Mgmd* mgmd = new Mgmd(i); mgmds.push_back(mgmd); CHECK(mgmd->start_from_config_ini(wd.path())); } // Connect the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->connect(config)); } // wait for confirmed config for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->wait_confirmed_config()); } // stop them for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->stop()); } // Check binary config files created CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); CHECK(file_exists(path(wd.path(), "ndb_2_config.bin.1", NULL).c_str())); CHECK(ConfigFactory::put(config, "ndb_mgmd", 1, "ArbitrationDelay", 100)); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config2.ini", NULL).c_str())); Uint32 no = 2; int loops = ctx->getNumLoops(); for (int l = 0; l < loops; l++, no++) { g_err << l << ": *** Reload from config.ini" << endl; for (unsigned i = 0; i < mgmds.size(); i++) { // Start from config2.ini CHECK(mgmds[i]->start_from_config_ini(wd.path(), (l & 1) == 1 ? "-f config.ini" : "-f config2.ini", "--reload", NULL)); } for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->connect(config)); CHECK(mgmds[i]->wait_confirmed_config()); } /** * Since it will first be confirmed... * and then once connected to other ndb_nmgmd start a config * change, it can take a bit until new config exists... * allow 30s */ Uint32 timeout = 30; for (unsigned i = 0; i < mgmds.size(); i++) { BaseString p = path(wd.path(), "", NULL); p.appfmt("ndb_%u_config.bin.%u", i+1, no); g_err << "CHECK(" << p.c_str() << ")" << endl; CHECK(file_exists(p.c_str(), timeout)); } for (unsigned i = 0; i < mgmds.size(); i++) { CHECK(mgmds[i]->stop()); } } return NDBT_OK; } static bool get_status(const char* connectstring, Properties& status) { NdbMgmd ndbmgmd; if (!ndbmgmd.connect(connectstring)) return false; Properties args; if (!ndbmgmd.call("get status", args, "node status", status, NULL, true)) { g_err << "fetch_mgmd_status: mgmd.call failed" << endl; return false; } return true; } static bool value_equal(Properties& status, int nodeid, const char* name, const char* expected_value) { const char* value; BaseString key; key.assfmt("node.%d.%s", nodeid, name); if (!status.get(key.c_str(), &value)) { g_err << "value_equal: no value found for '" << name << "." << nodeid << "'" << endl; return false; } if (strcmp(value, expected_value)) { g_err << "value_equal: found unexpected value: '" << value << "', expected: '" << expected_value << "'" < int runTestBug12352191(NDBT_Context* ctx, NDBT_Step* step) { BaseString version; version.assfmt("%u", NDB_VERSION_D); BaseString mysql_version; mysql_version.assfmt("%u", NDB_MYSQL_VERSION_D); BaseString address("127.0.0.1"); NDBT_Workingdir wd("test_mgmd"); // temporary working directory g_err << "** Create config.ini" << endl; Properties config = ConfigFactory::create(2); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); MgmdProcessList mgmds; const int nodeid1 = 1; Mgmd* mgmd1 = new Mgmd(nodeid1); mgmds.push_back(mgmd1); const int nodeid2 = 2; Mgmd* mgmd2 = new Mgmd(nodeid2); mgmds.push_back(mgmd2); // Start first mgmd CHECK(mgmd1->start_from_config_ini(wd.path())); CHECK(mgmd1->connect(config)); Properties status1; CHECK(get_status(mgmd1->connectstring(config).c_str(), status1)); //status1.print(); // Check status for own mgm node, always CONNECTED CHECK(value_equal(status1, nodeid1, "type", "MGM")); CHECK(value_equal(status1, nodeid1, "status", "CONNECTED")); CHECK(value_equal(status1, nodeid1, "version", version.c_str())); CHECK(value_equal(status1, nodeid1, "mysql_version", mysql_version.c_str())); CHECK(value_equal(status1, nodeid1, "address", address.c_str())); CHECK(value_equal(status1, nodeid1, "startphase", "0")); CHECK(value_equal(status1, nodeid1, "dynamic_id", "0")); CHECK(value_equal(status1, nodeid1, "node_group", "0")); CHECK(value_equal(status1, nodeid1, "connect_count", "0")); // Check status for other mgm node // not started yet -> NO_CONTACT, no address, no versions CHECK(value_equal(status1, nodeid2, "type", "MGM")); CHECK(value_equal(status1, nodeid2, "status", "NO_CONTACT")); CHECK(value_equal(status1, nodeid2, "version", "0")); CHECK(value_equal(status1, nodeid2, "mysql_version", "0")); CHECK(value_equal(status1, nodeid2, "address", "")); CHECK(value_equal(status1, nodeid2, "startphase", "0")); CHECK(value_equal(status1, nodeid2, "dynamic_id", "0")); CHECK(value_equal(status1, nodeid2, "node_group", "0")); CHECK(value_equal(status1, nodeid2, "connect_count", "0")); // Start second mgmd CHECK(mgmd2->start_from_config_ini(wd.path())); CHECK(mgmd2->connect(config)); // wait for confirmed config for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->wait_confirmed_config()); Properties status2; CHECK(get_status(mgmd2->connectstring(config).c_str(), status2)); //status2.print(); // Check status for own mgm node, always CONNECTED CHECK(value_equal(status2, nodeid2, "type", "MGM")); CHECK(value_equal(status2, nodeid2, "status", "CONNECTED")); CHECK(value_equal(status2, nodeid2, "version", version.c_str())); CHECK(value_equal(status2, nodeid2, "mysql_version", mysql_version.c_str())); CHECK(value_equal(status2, nodeid2, "address", address.c_str())); CHECK(value_equal(status2, nodeid2, "startphase", "0")); CHECK(value_equal(status2, nodeid2, "dynamic_id", "0")); CHECK(value_equal(status2, nodeid2, "node_group", "0")); CHECK(value_equal(status2, nodeid2, "connect_count", "0")); // Check status for other mgm node // both started now -> CONNECTED, address and versions filled in CHECK(value_equal(status2, nodeid1, "type", "MGM")); CHECK(value_equal(status2, nodeid1, "status", "CONNECTED")); CHECK(value_equal(status2, nodeid1, "version", version.c_str())); CHECK(value_equal(status2, nodeid1, "mysql_version", mysql_version.c_str())); CHECK(value_equal(status2, nodeid1, "address", address.c_str())); CHECK(value_equal(status2, nodeid1, "startphase", "0")); CHECK(value_equal(status2, nodeid1, "dynamic_id", "0")); CHECK(value_equal(status2, nodeid1, "node_group", "0")); CHECK(value_equal(status2, nodeid1, "connect_count", "0")); Properties status3; CHECK(get_status(mgmd1->connectstring(config).c_str(), status3)); //status3.print(); // Check status for own mgm node, always CONNECTED CHECK(value_equal(status3, nodeid1, "type", "MGM")); CHECK(value_equal(status3, nodeid1, "status", "CONNECTED")); CHECK(value_equal(status3, nodeid1, "version", version.c_str())); CHECK(value_equal(status3, nodeid1, "mysql_version", mysql_version.c_str())); CHECK(value_equal(status3, nodeid1, "address", address.c_str())); CHECK(value_equal(status3, nodeid1, "startphase", "0")); CHECK(value_equal(status3, nodeid1, "dynamic_id", "0")); CHECK(value_equal(status3, nodeid1, "node_group", "0")); CHECK(value_equal(status3, nodeid1, "connect_count", "0")); // Check status for other mgm node // both started now -> CONNECTED, address and versions filled in CHECK(value_equal(status3, nodeid2, "type", "MGM")); CHECK(value_equal(status3, nodeid2, "status", "CONNECTED")); CHECK(value_equal(status3, nodeid2, "version", version.c_str())); CHECK(value_equal(status3, nodeid2, "mysql_version", mysql_version.c_str())); CHECK(value_equal(status3, nodeid2, "address", address.c_str())); CHECK(value_equal(status3, nodeid2, "startphase", "0")); CHECK(value_equal(status3, nodeid2, "dynamic_id", "0")); CHECK(value_equal(status3, nodeid2, "node_group", "0")); CHECK(value_equal(status3, nodeid2, "connect_count", "0")); return NDBT_OK; } int runBug61607(NDBT_Context* ctx, NDBT_Step* step) { NDBT_Workingdir wd("test_mgmd"); // temporary working directory // Create config.ini const int cnt_mgmd = 1; Properties config = ConfigFactory::create(cnt_mgmd); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); // Start ndb_mgmd(s) MgmdProcessList mgmds; for (int i = 1; i <= cnt_mgmd; i++) { Mgmd* mgmd = new Mgmd(i); mgmds.push_back(mgmd); CHECK(mgmd->start_from_config_ini(wd.path())); } // Connect the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->connect(config)); // wait for confirmed config for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->wait_confirmed_config()); // Check binary config files created CHECK(file_exists(path(wd.path(), "ndb_1_config.bin.1", NULL).c_str())); int no_of_nodes = 0; int * node_ids = 0; int initialstart = 0; int nostart = 0; int abort = 0; int force = 0; int need_disconnect = 0; int res = ndb_mgm_restart4(mgmds[0]->handle(), no_of_nodes, node_ids, initialstart, nostart, abort, force, &need_disconnect); return res == 0 ? NDBT_OK : NDBT_FAILED; } int runStopDuringStart(NDBT_Context* ctx, NDBT_Step* step) { MgmdProcessList mgmds; NDBT_Workingdir wd("test_mgmd"); // temporary working directory // Create config.ini unsigned nodeids[] = { 251, 252 }; Properties config = ConfigFactory::create(2, 1, 1, nodeids); CHECK(ConfigFactory::write_config_ini(config, path(wd.path(), "config.ini", NULL).c_str())); for (unsigned i = 0; i < NDB_ARRAY_SIZE(nodeids); i++) { Mgmd* mgmd = new Mgmd(nodeids[i]); mgmds.push_back(mgmd); CHECK(mgmd->start_from_config_ini(wd.path())); } // Connect the ndb_mgmd(s) for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->connect(config)); // wait for confirmed config for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->wait_confirmed_config()); // Check binary config files created for (unsigned i = 0; i < mgmds.size(); i++) { BaseString file; file.assfmt("ndb_%u_config.bin.1", nodeids[i]); CHECK(file_exists(path(wd.path(), file.c_str(), NULL).c_str())); } // stop them for (unsigned i = 0; i < mgmds.size(); i++) { mgmds[i]->stop(); int exitCode; mgmds[i]->wait(exitCode); } // restart one with error-insert 100 // => it shall exit during start... mgmds[0]->start(wd.path(), "--error-insert=100", NULL); // restart rest normally for (unsigned i = 1; i < mgmds.size(); i++) { mgmds[i]->start(wd.path()); } // wait first one to terminate int exitCode; mgmds[0]->wait(exitCode); NdbSleep_MilliSleep(3000); // check other OK for (unsigned i = 1; i < mgmds.size(); i++) { CHECK(mgmds[i]->connect(config)); CHECK(mgmds[i]->wait_confirmed_config()); } // now restart without error insert mgmds[0]->start(wd.path()); // connect CHECK(mgmds[0]->connect(config)); // all should be ok for (unsigned i = 0; i < mgmds.size(); i++) CHECK(mgmds[i]->wait_confirmed_config()); return NDBT_OK; } NDBT_TESTSUITE(testMgmd); DRIVER(DummyDriver); /* turn off use of NdbApi */ TESTCASE("Basic2Mgm", "Basic test with two mgmd") { INITIALIZER(runTestBasic2Mgm); } TESTCASE("Bug42015", "Test that mgmd can fetch configuration from another mgmd") { INITIALIZER(runTestBug42015); } TESTCASE("NowaitNodes", "Test that one mgmd(of 2) can start alone with usage " "of --nowait-nodes, then start the second mgmd and it should join") { INITIALIZER(runTestNowaitNodes); } TESTCASE("NowaitNodes2", "Test that one mgmd(of 2) can start alone with usage " "of --nowait-nodes, then start the second mgmd from different " "configuration and the one with lowest nodeid should shutdown") { INITIALIZER(runTestNowaitNodes2); } TESTCASE("NoCfgCache", "Test that when an mgmd is started with --skip-config-cache, " "no ndb_xx_config.xx.bin file is created, but you can " "connect to the mgm node and retrieve the config.") { INITIALIZER(runTestNoConfigCache); } TESTCASE("NoCfgCacheOrConfigDir", "Test that when an mgmd is started with --skip-config-cache, " "no ndb_xx_config.xx.bin file is created, but you can " "connect to the mgm node and retrieve the config.") { INITIALIZER(runTestNoConfigCache_DontCreateConfigDir); } TESTCASE("NoCfgCacheFetch", "Test that when an mgmd is started with --skip-config-cache, " "it can still fetch config from another ndb_mgmd.") { INITIALIZER(runTestNoConfigCache_Fetch); } TESTCASE("Bug45495", "Test that mgmd can be restarted in any order") { INITIALIZER(runTestBug45495); } TESTCASE("Bug56844", "Test that mgmd can be reloaded in parallel") { INITIALIZER(runBug56844); } TESTCASE("Bug12352191", "Test mgmd status for other mgmd") { INITIALIZER(runTestBug12352191); } TESTCASE("Bug61607", "ndb_mgmd incorrectly reports failure when there are no ndbds to stop") { INITIALIZER(runBug61607); } TESTCASE("StopDuringStart", "") { INITIALIZER(runStopDuringStart); } NDBT_TESTSUITE_END(testMgmd) int main(int argc, const char** argv) { ndb_init(); NDBT_TESTSUITE_INSTANCE(testMgmd); testMgmd.setCreateTable(false); testMgmd.setRunAllTables(true); testMgmd.setConnectCluster(false); #ifdef NDB_USE_GET_ENV char buf1[255], buf2[255]; if (NdbEnv_GetEnv("NDB_MGMD_VALGRIND_EXE", buf1, sizeof(buf1))) { exe_valgrind = buf1; } if (NdbEnv_GetEnv("NDB_MGMD_VALGRIND_ARG", buf2, sizeof(buf2))) { arg_valgrind = buf2; } #endif return testMgmd.execute(argc, argv); } template class Vector;