/**
         * 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());
            }
        }
Exemple #2
0
void Balancer::_ping(OperationContext* txn, bool waiting) {
    MongosType mType;
    mType.setName(_myid);
    mType.setPing(jsTime());
    mType.setUptime(_timer.seconds());
    mType.setWaiting(waiting);
    mType.setMongoVersion(versionString);

    grid.catalogManager(txn)->updateConfigDocument(txn,
                                                   MongosType::ConfigNS,
                                                   BSON(MongosType::name(_myid)),
                                                   BSON("$set" << mType.toBSON()),
                                                   true);
}
    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();
    }