/** * Stores sample shard and ping information at the current version. */ void storeShardsAndPings(int numShards, int numPings) { DBDirectClient client(&_txn); for (int i = 0; i < numShards; i++) { ShardType shard; shard.setName(OID::gen().toString()); shard.setHost((string) (str::stream() << "$dummyShard:" << (i + 1) << "0000")); client.insert(ShardType::ConfigNS, shard.toBSON()); } for (int i = 0; i < numPings; i++) { MongosType ping; ping.setName((string) (str::stream() << "$dummyMongos:" << (i + 1) << "0000")); ping.setPing(jsTime()); ping.setMongoVersion(versionString); ping.setConfigVersion(CURRENT_CONFIG_VERSION); if (i % 2 == 0) { ping.setPing(ping.getPing() - Minutes(10)); } client.insert(MongosType::ConfigNS, ping.toBSON()); } }
Status checkClusterMongoVersions(const ConnectionString& configLoc, const string& minMongoVersion) { scoped_ptr<ScopedDbConnection> connPtr; // // Find mongos pings in config server // try { connPtr.reset(new ScopedDbConnection(configLoc, 30)); ScopedDbConnection& conn = *connPtr; scoped_ptr<DBClientCursor> cursor(_safeCursor(conn->query(MongosType::ConfigNS, Query()))); while (cursor->more()) { BSONObj pingDoc = cursor->next(); MongosType ping; string errMsg; // NOTE: We don't care if the ping is invalid, legacy stuff will be if (!ping.parseBSON(pingDoc, &errMsg)) { warning() << "could not parse ping document: " << pingDoc << causedBy(errMsg) << endl; continue; } string mongoVersion = "2.0"; // Hack to determine older mongos versions from ping format if (ping.isWaitingSet()) mongoVersion = "2.2"; if (ping.isMongoVersionSet() && ping.getMongoVersion() != "") { mongoVersion = ping.getMongoVersion(); } Date_t lastPing = ping.getPing(); long long quietIntervalMillis = 0; Date_t currentJsTime = jsTime(); if (currentJsTime >= lastPing) { quietIntervalMillis = static_cast<long long>(currentJsTime - lastPing); } long long quietIntervalMins = quietIntervalMillis / (60 * 1000); // We assume that anything that hasn't pinged in 5 minutes is probably down if (quietIntervalMins >= 5) { log() << "stale mongos detected " << quietIntervalMins << " minutes ago," << " network location is " << pingDoc["_id"].String() << ", not checking version" << endl; } else { if (versionCmp(mongoVersion, minMongoVersion) < 0) { return Status(ErrorCodes::RemoteValidationError, stream() << "version " << mongoVersion << " of mongos at " << ping.getName() << " is not compatible with the config update, " << "you must wait 5 minutes " << "after shutting down a pre-" << minMongoVersion << " mongos"); } } } } catch (const DBException& e) { return e.toStatus("could not read mongos pings collection"); } // // Load shards from config server // vector<ConnectionString> shardLocs; try { ScopedDbConnection& conn = *connPtr; scoped_ptr<DBClientCursor> cursor(_safeCursor(conn->query(ShardType::ConfigNS, Query()))); while (cursor->more()) { BSONObj shardDoc = cursor->next(); ShardType shard; string errMsg; if (!shard.parseBSON(shardDoc, &errMsg) || !shard.isValid(&errMsg)) { connPtr->done(); return Status(ErrorCodes::UnsupportedFormat, stream() << "invalid shard " << shardDoc << " read from the config server" << causedBy(errMsg)); } ConnectionString shardLoc = ConnectionString::parse(shard.getHost(), errMsg); if (shardLoc.type() == ConnectionString::INVALID) { connPtr->done(); return Status(ErrorCodes::UnsupportedFormat, stream() << "invalid shard host " << shard.getHost() << " read from the config server" << causedBy(errMsg)); } shardLocs.push_back(shardLoc); } } catch (const DBException& e) { return e.toStatus("could not read shards collection"); } connPtr->done(); // // We've now got all the shard info from the config server, start contacting the shards // and verifying their versions. // for (vector<ConnectionString>::iterator it = shardLocs.begin(); it != shardLocs.end(); ++it) { ConnectionString& shardLoc = *it; vector<HostAndPort> servers = shardLoc.getServers(); for (vector<HostAndPort>::iterator serverIt = servers.begin(); serverIt != servers.end(); ++serverIt) { // Note: This will *always* be a single-host connection ConnectionString serverLoc(*serverIt); log() << "checking that version of host " << serverLoc << " is compatible with " << minMongoVersion << endl; scoped_ptr<ScopedDbConnection> serverConnPtr; bool resultOk; BSONObj buildInfo; try { serverConnPtr.reset(new ScopedDbConnection(serverLoc, 30)); ScopedDbConnection& serverConn = *serverConnPtr; resultOk = serverConn->runCommand("admin", BSON("buildInfo" << 1), buildInfo); } catch (const DBException& e) { warning() << "could not run buildInfo command on " << serverLoc.toString() << causedBy(e) << ", you must manually verify this mongo server is " << "offline (for at least 5 minutes) or of a version >= 2.2" << endl; continue; } // TODO: Make running commands saner such that we can consolidate error handling if (!resultOk) { return Status(ErrorCodes::UnknownError, stream() << DBClientConnection::getLastErrorString(buildInfo) << causedBy(buildInfo.toString())); } serverConnPtr->done(); verify(buildInfo["version"].type() == String); string mongoVersion = buildInfo["version"].String(); if (versionCmp(mongoVersion, minMongoVersion) < 0) { return Status(ErrorCodes::RemoteValidationError, stream() << "version " << mongoVersion << " of mongo server at " << serverLoc.toString() << " is not compatible with the config update"); } } } return Status::OK(); }
Status checkClusterMongoVersions(CatalogManager* catalogManager, const string& minMongoVersion) { scoped_ptr<ScopedDbConnection> connPtr; // // Find mongos pings in config server // try { connPtr.reset(new ScopedDbConnection(catalogManager->connectionString(), 30)); ScopedDbConnection& conn = *connPtr; scoped_ptr<DBClientCursor> cursor(_safeCursor(conn->query(MongosType::ConfigNS, Query()))); while (cursor->more()) { BSONObj pingDoc = cursor->next(); MongosType ping; string errMsg; // NOTE: We don't care if the ping is invalid, legacy stuff will be if (!ping.parseBSON(pingDoc, &errMsg)) { warning() << "could not parse ping document: " << pingDoc << causedBy(errMsg) << endl; continue; } string mongoVersion = "2.0"; // Hack to determine older mongos versions from ping format if (ping.isWaitingSet()) mongoVersion = "2.2"; if (ping.isMongoVersionSet() && ping.getMongoVersion() != "") { mongoVersion = ping.getMongoVersion(); } Date_t lastPing = ping.getPing(); Minutes quietIntervalMins{0}; Date_t currentJsTime = jsTime(); if (currentJsTime >= lastPing) { quietIntervalMins = duration_cast<Minutes>(currentJsTime - lastPing); } // We assume that anything that hasn't pinged in 5 minutes is probably down if (quietIntervalMins >= Minutes{5}) { log() << "stale mongos detected " << quietIntervalMins.count() << " minutes ago, network location is " << pingDoc["_id"].String() << ", not checking version"; } else { if (versionCmp(mongoVersion, minMongoVersion) < 0) { return Status(ErrorCodes::RemoteValidationError, stream() << "version " << mongoVersion << " detected on mongos at " << ping.getName() << ", but version >= " << minMongoVersion << " required; you must wait 5 minutes " << "after shutting down a pre-" << minMongoVersion << " mongos"); } } } } catch (const DBException& e) { return e.toStatus("could not read mongos pings collection"); } connPtr->done(); // // Load shards from config server // vector<HostAndPort> servers; try { vector<ShardType> shards; Status status = catalogManager->getAllShards(&shards); if (!status.isOK()) { return status; } for (const ShardType& shard : shards) { Status status = shard.validate(); if (!status.isOK()) { return Status(ErrorCodes::UnsupportedFormat, stream() << "shard " << shard.toBSON() << " failed validation: " << causedBy(status)); } const auto shardConnStatus = ConnectionString::parse(shard.getHost()); if (!shardConnStatus.isOK()) { return Status(ErrorCodes::UnsupportedFormat, stream() << "invalid shard host " << shard.getHost() << " read from the config server" << shardConnStatus.getStatus().toString()); } vector<HostAndPort> shardServers = shardConnStatus.getValue().getServers(); servers.insert(servers.end(), shardServers.begin(), shardServers.end()); } } catch (const DBException& e) { return e.toStatus("could not read shards collection"); } // Add config servers to list of servers to check version against vector<HostAndPort> configServers = catalogManager->connectionString().getServers(); servers.insert(servers.end(), configServers.begin(), configServers.end()); // // We've now got all the shard info from the config server, start contacting the shards // and config servers and verifying their versions. // for (vector<HostAndPort>::iterator serverIt = servers.begin(); serverIt != servers.end(); ++serverIt) { // Note: This will *always* be a single-host connection ConnectionString serverLoc(*serverIt); dassert(serverLoc.type() == ConnectionString::MASTER || serverLoc.type() == ConnectionString::CUSTOM); // for dbtests log() << "checking that version of host " << serverLoc << " is compatible with " << minMongoVersion << endl; scoped_ptr<ScopedDbConnection> serverConnPtr; bool resultOk; BSONObj buildInfo; try { serverConnPtr.reset(new ScopedDbConnection(serverLoc, 30)); ScopedDbConnection& serverConn = *serverConnPtr; resultOk = serverConn->runCommand("admin", BSON("buildInfo" << 1), buildInfo); } catch (const DBException& e) { warning() << "could not run buildInfo command on " << serverLoc.toString() << " " << causedBy(e) << ". Please ensure that this server is up and at a " "version >= " << minMongoVersion; continue; } // TODO: Make running commands saner such that we can consolidate error handling if (!resultOk) { return Status(ErrorCodes::UnknownError, stream() << DBClientConnection::getLastErrorString(buildInfo) << causedBy(buildInfo.toString())); } serverConnPtr->done(); verify(buildInfo["version"].type() == String); string mongoVersion = buildInfo["version"].String(); if (versionCmp(mongoVersion, minMongoVersion) < 0) { return Status(ErrorCodes::RemoteValidationError, stream() << "version " << mongoVersion << " detected on mongo " "server at " << serverLoc.toString() << ", but version >= " << minMongoVersion << " required"); } } return Status::OK(); }