From: magnus.blaudd Date: June 21 2011 1:12pm Subject: bzr commit into mysql-5.1-telco-7.0 branch (magnus.blaudd:4469) Bug#48301 Bug#12352191 List-Archive: http://lists.mysql.com/commits/139564 X-Bug: 48301,12352191 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============7147602492858949638==" --===============7147602492858949638== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///data0/magnus/mysql/7.0-bug12352191/ based on revid:ole.john.aske@stripped 4469 magnus.blaudd@stripped 2011-06-21 Bug#12352191 NDB_MGMD NODES DON'T SEE EACH OTHER AS CONNECTED UNTIL END OF SYSTEM RESTART. - Add specialized function status_mgmd() and status_api() used to retrive version and status for API and MGM nodes. - Make status_mgmd() aware that other MGM nodes are connected directly to own node with transporter - Fix missing assignment of mysql_version in Qmgr - Return the connected adress from transporter also for MGM nodes - Also fix bug#48301 by adding a new status CONNECTED to be used when we know the node is connected(this stats was previously only indicated by version != 0) modified: storage/ndb/include/mgmapi/mgmapi.h storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp storage/ndb/src/mgmapi/mgmapi.cpp storage/ndb/src/mgmsrv/MgmtSrvr.cpp storage/ndb/src/mgmsrv/MgmtSrvr.hpp storage/ndb/src/ndbapi/ClusterMgr.cpp storage/ndb/test/ndbapi/testMgmd.cpp === modified file 'storage/ndb/include/mgmapi/mgmapi.h' --- a/storage/ndb/include/mgmapi/mgmapi.h 2011-02-01 23:27:25 +0000 +++ b/storage/ndb/include/mgmapi/mgmapi.h 2011-06-21 13:10:37 +0000 @@ -206,11 +206,13 @@ extern "C" { NDB_MGM_NODE_STATUS_SINGLEUSER = 7, /** Resume mode*/ NDB_MGM_NODE_STATUS_RESUME = 8, + /** Node is connected */ + NDB_MGM_NODE_STATUS_CONNECTED = 9, #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL /** Min valid value*/ NDB_MGM_NODE_STATUS_MIN = 0, /** Max valid value*/ - NDB_MGM_NODE_STATUS_MAX = 8 + NDB_MGM_NODE_STATUS_MAX = 9 #endif }; === modified file 'storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp' --- a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp 2011-06-13 06:14:32 +0000 +++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp 2011-06-21 13:10:37 +0000 @@ -3728,6 +3728,7 @@ Qmgr::execAPI_VERSION_REQ(Signal * signa else { conf->version = 0; + conf->mysql_version = 0; conf->inet_addr= 0; } conf->nodeId = nodeId; === modified file 'storage/ndb/src/mgmapi/mgmapi.cpp' --- a/storage/ndb/src/mgmapi/mgmapi.cpp 2011-03-28 09:01:03 +0000 +++ b/storage/ndb/src/mgmapi/mgmapi.cpp 2011-06-21 13:10:37 +0000 @@ -934,7 +934,10 @@ static struct ndb_mgm_status_atoi status { "STARTED", NDB_MGM_NODE_STATUS_STARTED }, { "SHUTTING_DOWN", NDB_MGM_NODE_STATUS_SHUTTING_DOWN }, { "RESTARTING", NDB_MGM_NODE_STATUS_RESTARTING }, - { "SINGLE USER MODE", NDB_MGM_NODE_STATUS_SINGLEUSER } + { "SINGLE USER MODE", NDB_MGM_NODE_STATUS_SINGLEUSER }, + { "SINGLE USER MODE", NDB_MGM_NODE_STATUS_SINGLEUSER }, + { "RESUME", NDB_MGM_NODE_STATUS_RESUME }, + { "CONNECTED", NDB_MGM_NODE_STATUS_CONNECTED } }; const int no_of_status_values = (sizeof(status_values) / === modified file 'storage/ndb/src/mgmsrv/MgmtSrvr.cpp' --- a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp 2011-06-01 07:40:49 +0000 +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp 2011-06-21 13:10:37 +0000 @@ -835,59 +835,34 @@ MgmtSrvr::start(int nodeId) * Version handling *****************************************************************************/ -int -MgmtSrvr::versionNode(int nodeId, Uint32 &version, Uint32& mysql_version, - const char **address) -{ - version= 0; - mysql_version = 0; - if (getOwnNodeId() == nodeId) - { - /** - * If we're inquiring about our own node id, - * We know what version we are (version implies connected for mgm) - * but would like to find out from elsewhere what address they're using - * to connect to us. This means that secondary mgm servers - * can list ip addresses for mgm servers. - * - * If we don't get an address (i.e. no db nodes), - * we get the address from the configuration. - */ - sendVersionReq(nodeId, version, mysql_version, address); - version= NDB_VERSION; - mysql_version = NDB_MYSQL_VERSION_D; - if(!*address) - { - Guard g(m_local_config_mutex); - ConfigIter iter(m_local_config, CFG_SECTION_NODE); - unsigned tmp= 0; - for(iter.first();iter.valid();iter.next()) - { - if(iter.get(CFG_NODE_ID, &tmp)) require(false); - if((unsigned)nodeId!=tmp) - continue; - if(iter.get(CFG_NODE_HOST, address)) require(false); - break; - } - } +void +MgmtSrvr::status_api(int nodeId, + ndb_mgm_node_status& node_status, + Uint32& version, Uint32& mysql_version, + const char **address) +{ + assert(getNodeType(nodeId) == NDB_MGM_NODE_TYPE_API); + assert(version == 0 && mysql_version == 0); + + if (sendVersionReq(nodeId, version, mysql_version, address) != 0) + { + // Couldn't get version from any NDB node. + assert(version == 0); + node_status = NDB_MGM_NODE_STATUS_UNKNOWN; + return; } - else if (getNodeType(nodeId) == NDB_MGM_NODE_TYPE_NDB) + + if (version) { - trp_node node = getNodeInfo(nodeId); - if(node.is_connected()) - { - version= node.m_info.m_version; - mysql_version = node.m_info.m_mysql_version; - } - *address= get_connect_address(nodeId); + assert(mysql_version); + node_status = NDB_MGM_NODE_STATUS_CONNECTED; } - else if (getNodeType(nodeId) == NDB_MGM_NODE_TYPE_API || - getNodeType(nodeId) == NDB_MGM_NODE_TYPE_MGM) + else { - return sendVersionReq(nodeId, version, mysql_version, address); + assert(mysql_version == 0); + node_status = NDB_MGM_NODE_STATUS_NO_CONTACT; } - - return 0; + return; } @@ -1898,6 +1873,75 @@ MgmtSrvr::updateStatus() theFacade->ext_forceHB(); } + +void +MgmtSrvr::status_mgmd(NodeId node_id, + ndb_mgm_node_status& node_status, + Uint32& version, Uint32& mysql_version, + const char **address) +{ + assert(getNodeType(node_id) == NDB_MGM_NODE_TYPE_MGM); + + if (node_id == getOwnNodeId()) + { + /* + Special case to get version of own node + - version and mysql_version is hardcoded + - address should be the address seen from ndbd(if it's connected) + else use HostName from config + */ + Uint32 tmp_version = 0, tmp_mysql_version = 0; + sendVersionReq(node_id, tmp_version, tmp_mysql_version, address); + // Check that the version returned is equal to compiled in version + assert(tmp_version == 0 || + (tmp_version == NDB_VERSION && + tmp_mysql_version == NDB_MYSQL_VERSION_D)); + + version = NDB_VERSION; + mysql_version = NDB_MYSQL_VERSION_D; + if(!*address) + { + // No address returned from ndbd -> get HostName from config + Guard g(m_local_config_mutex); + ConfigIter iter(m_local_config, CFG_SECTION_NODE); + require(iter.find(CFG_NODE_ID, node_id) == 0); + require(iter.get(CFG_NODE_HOST, address) == 0); + + /* + Try to convert HostName to numerical ip address + (to get same output as if ndbd had replied) + */ + struct in_addr addr; + if (Ndb_getInAddr(&addr, *address) == 0) + *address = inet_ntoa(addr); + } + + node_status = NDB_MGM_NODE_STATUS_CONNECTED; + return; + } + + /* + MGM nodes are connected directly to all other MGM + node(s), return status as seen by ClusterMgr + */ + const trp_node node = getNodeInfo(node_id); + if(node.is_connected()) + { + version = node.m_info.m_version; + mysql_version = node.m_info.m_mysql_version; + node_status = NDB_MGM_NODE_STATUS_CONNECTED; + *address= get_connect_address(node_id); + } + else + { + version = 0; + mysql_version = 0; + node_status = NDB_MGM_NODE_STATUS_NO_CONTACT; + } + + return; +} + int MgmtSrvr::status(int nodeId, ndb_mgm_node_status * _status, @@ -1910,24 +1954,38 @@ MgmtSrvr::status(int nodeId, Uint32 * connectCount, const char **address) { - if (getNodeType(nodeId) == NDB_MGM_NODE_TYPE_API || - getNodeType(nodeId) == NDB_MGM_NODE_TYPE_MGM) { - versionNode(nodeId, *version, *mysql_version, address); - } else { - *address= get_connect_address(nodeId); + switch(getNodeType(nodeId)){ + case NDB_MGM_NODE_TYPE_API: + status_api(nodeId, *_status, *version, *mysql_version, address); + return 0; + break; + + case NDB_MGM_NODE_TYPE_MGM: + status_mgmd(nodeId, *_status, *version, *mysql_version, address); + return 0; + break; + + case NDB_MGM_NODE_TYPE_NDB: + break; + + default: + abort(); + break; } const trp_node node = getNodeInfo(nodeId); + assert(getNodeType(nodeId) == NDB_MGM_NODE_TYPE_NDB && + node.m_info.getType() == NodeInfo::DB); if(!node.is_connected()){ * _status = NDB_MGM_NODE_STATUS_NO_CONTACT; return 0; } - - if (getNodeType(nodeId) == NDB_MGM_NODE_TYPE_NDB) { - * version = node.m_info.m_version; - * mysql_version = node.m_info.m_mysql_version; - } + + * version = node.m_info.m_version; + * mysql_version = node.m_info.m_mysql_version; + + *address= get_connect_address(nodeId); * dynamic = node.m_state.dynamicId; * nodegroup = node.m_state.nodeGroup; @@ -2792,9 +2850,10 @@ MgmtSrvr::getNodeType(NodeId nodeId) con const char *MgmtSrvr::get_connect_address(Uint32 node_id) { - if (m_connect_address[node_id].s_addr == 0 && - theFacade && - getNodeType(node_id) == NDB_MGM_NODE_TYPE_NDB) + if (theFacade && + m_connect_address[node_id].s_addr == 0 && + (getNodeType(node_id) == NDB_MGM_NODE_TYPE_MGM || + getNodeType(node_id) == NDB_MGM_NODE_TYPE_NDB)) { const trp_node &node= getNodeInfo(node_id); if (node.is_connected()) === modified file 'storage/ndb/src/mgmsrv/MgmtSrvr.hpp' --- a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp 2011-06-01 07:40:49 +0000 +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp 2011-06-21 13:10:37 +0000 @@ -364,8 +364,14 @@ public: private: int guess_master_node(SignalSender&); - int versionNode(int nodeId, Uint32 &version, - Uint32 &mysql_version, const char **address); + void status_api(int nodeId, + ndb_mgm_node_status& node_status, + Uint32& version, Uint32& mysql_version, + const char **address); + void status_mgmd(NodeId node_id, + ndb_mgm_node_status& node_status, + Uint32& version, Uint32& mysql_version, + const char **address); int sendVersionReq(int processId, Uint32 &version, Uint32& mysql_version, const char **address); === modified file 'storage/ndb/src/ndbapi/ClusterMgr.cpp' --- a/storage/ndb/src/ndbapi/ClusterMgr.cpp 2011-02-03 14:20:36 +0000 +++ b/storage/ndb/src/ndbapi/ClusterMgr.cpp 2011-06-21 13:10:37 +0000 @@ -651,6 +651,9 @@ ClusterMgr::execAPI_REGREQ(const Uint32 if(node.m_info.m_version != apiRegReq->version){ node.m_info.m_version = apiRegReq->version; + node.m_info.m_mysql_version = apiRegReq->mysql_version; + if (node.m_info.m_version < NDBD_SPLIT_VERSION) + node.m_info.m_mysql_version = 0; if (getMajor(node.m_info.m_version) < getMajor(NDB_VERSION) || getMinor(node.m_info.m_version) < getMinor(NDB_VERSION)) { === modified file 'storage/ndb/test/ndbapi/testMgmd.cpp' --- a/storage/ndb/test/ndbapi/testMgmd.cpp 2011-02-03 14:20:36 +0000 +++ b/storage/ndb/test/ndbapi/testMgmd.cpp 2011-06-21 13:10:37 +0000 @@ -934,6 +934,171 @@ runBug56844(NDBT_Context* ctx, NDBT_Step 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; + +} + NDBT_TESTSUITE(testMgmd); DRIVER(DummyDriver); /* turn off use of NdbApi */ @@ -981,6 +1146,11 @@ TESTCASE("Bug56844", { INITIALIZER(runBug56844); } +TESTCASE("Bug12352191", + "Test mgmd status for other mgmd") +{ + INITIALIZER(runTestBug12352191); +} NDBT_TESTSUITE_END(testMgmd); --===============7147602492858949638== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/magnus.blaudd@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: magnus.blaudd@stripped\ # hr0vnnxhqduwsr07 # target_branch: file:///data0/magnus/mysql/7.0-bug12352191/ # testament_sha1: 65006757fb5c9a1a4a7484e90ec68299784c805f # timestamp: 2011-06-21 15:12:16 +0200 # base_revision_id: ole.john.aske@stripped\ # 23uh12xi5czgoc5z # # Begin bundle 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 --===============7147602492858949638==--