BSONObj ShardIdentityType::toBSON() const { BSONObjBuilder builder; builder.append("_id", IdName); if (_configsvrConnString) { builder << configsvrConnString(_configsvrConnString->toString()); } if (_shardName) { builder << shardName(_shardName.get()); } if (_clusterId) { builder << clusterId(_clusterId.get()); } return builder.obj(); }
Status ShardIdentityType::validate() const { if (!_configsvrConnString) { return {ErrorCodes::NoSuchKey, str::stream() << "missing " << configsvrConnString() << " field"}; } if (_configsvrConnString->type() != ConnectionString::SET) { return {ErrorCodes::UnsupportedFormat, str::stream() << "config connection string can only be replica sets, got " << ConnectionString::typeToString(_configsvrConnString->type())}; } if (!_shardName || _shardName->empty()) { return {ErrorCodes::NoSuchKey, str::stream() << "missing " << shardName() << " field"}; } if (!_clusterId || !_clusterId->isSet()) { return {ErrorCodes::NoSuchKey, str::stream() << "missing " << clusterId() << " field"}; } return Status::OK(); }
StatusWith<ShardIdentityType> ShardIdentityType::fromBSON(const BSONObj& source) { if (!source.hasField("_id")) { return {ErrorCodes::NoSuchKey, str::stream() << "missing _id field for shardIdentity document"}; } ShardIdentityType shardIdentity; { std::string docId; Status status = bsonExtractStringField(source, "_id", &docId); if (!status.isOK()) { return status; } if (docId != IdName) { return {ErrorCodes::FailedToParse, str::stream() << "got _id: " << docId << " instead of " << IdName}; } } { std::string connString; Status status = bsonExtractStringField(source, configsvrConnString(), &connString); if (!status.isOK()) { return status; } try { // Note: ConnectionString::parse can uassert from HostAndPort constructor. auto parsedConfigConnStrStatus = ConnectionString::parse(connString); if (!parsedConfigConnStrStatus.isOK()) { return parsedConfigConnStrStatus.getStatus(); } auto configSvrConnStr = parsedConfigConnStrStatus.getValue(); if (configSvrConnStr.type() != ConnectionString::SET) { return Status(ErrorCodes::UnsupportedFormat, str::stream() << "config server connection string can only be replica sets: " << configSvrConnStr.toString()); } shardIdentity.setConfigsvrConnString(std::move(configSvrConnStr)); } catch (const UserException& parseException) { return parseException.toStatus(); } } { std::string name; Status status = bsonExtractStringField(source, shardName(), &name); if (!status.isOK()) { return status; } shardIdentity.setShardName(name); } { OID oid; Status status = bsonExtractOIDField(source, clusterId(), &oid); if (!status.isOK()) { return status; } shardIdentity.setClusterId(oid); } return shardIdentity; }
StatusWith<string> isValidShard(const string& name, const ConnectionString& shardConnectionString, ScopedDbConnection& conn) { if (conn->type() == ConnectionString::SYNC) { return Status(ErrorCodes::BadValue, "can't use sync cluster as a shard; for a replica set, " "you have to use <setname>/<server1>,<server2>,..."); } BSONObj resIsMongos; // (ok == 0) implies that it is a mongos if (conn->runCommand("admin", BSON("isdbgrid" << 1), resIsMongos)) { return Status(ErrorCodes::BadValue, "can't add a mongos process as a shard"); } BSONObj resIsMaster; if (!conn->runCommand("admin", BSON("isMaster" << 1), resIsMaster)) { return Status(ErrorCodes::OperationFailed, str::stream() << "failed running isMaster: " << resIsMaster); } // if the shard has only one host, make sure it is not part of a replica set string setName = resIsMaster["setName"].str(); string commandSetName = shardConnectionString.getSetName(); if (commandSetName.empty() && !setName.empty()) { return Status(ErrorCodes::BadValue, str::stream() << "host is part of set " << setName << "; " << "use replica set url format " << "<setname>/<server1>,<server2>, ..."); } if (!commandSetName.empty() && setName.empty()) { return Status(ErrorCodes::OperationFailed, str::stream() << "host did not return a set name; " << "is the replica set still initializing? " << resIsMaster); } // if the shard is part of replica set, make sure it is the right one if (!commandSetName.empty() && (commandSetName != setName)) { return Status(ErrorCodes::OperationFailed, str::stream() << "host is part of a different set: " << setName); } if (setName.empty()) { // check this isn't a --configsvr BSONObj res; bool ok = conn->runCommand("admin", BSON("replSetGetStatus" << 1), res); if(!ok && res["info"].type() == String && res["info"].String() == "configsvr") { return Status(ErrorCodes::BadValue, "the specified mongod is a --configsvr and " "should thus not be a shard server"); } } // if the shard is part of a replica set, // make sure all the hosts mentioned in 'shardConnectionString' are part of // the set. It is fine if not all members of the set are present in 'shardConnectionString'. bool foundAll = true; string offendingHost; if (!commandSetName.empty()) { set<string> hostSet; BSONObjIterator iter(resIsMaster["hosts"].Obj()); while (iter.more()) { hostSet.insert(iter.next().String()); // host:port } if (resIsMaster["passives"].isABSONObj()) { BSONObjIterator piter(resIsMaster["passives"].Obj()); while (piter.more()) { hostSet.insert(piter.next().String()); // host:port } } if (resIsMaster["arbiters"].isABSONObj()) { BSONObjIterator piter(resIsMaster["arbiters"].Obj()); while (piter.more()) { hostSet.insert(piter.next().String()); // host:port } } vector<HostAndPort> hosts = shardConnectionString.getServers(); for (size_t i = 0; i < hosts.size(); i++) { if (!hosts[i].hasPort()) { hosts[i] = HostAndPort(hosts[i].host(), hosts[i].port()); } string host = hosts[i].toString(); // host:port if (hostSet.find(host) == hostSet.end()) { offendingHost = host; foundAll = false; break; } } } if (!foundAll) { return Status(ErrorCodes::OperationFailed, str::stream() << "in seed list " << shardConnectionString.toString() << ", host " << offendingHost << " does not belong to replica set " << setName); } string shardName(name); // shard name defaults to the name of the replica set if (name.empty() && !setName.empty()) { shardName = setName; } // disallow adding shard replica set with name 'config' if (shardName == "config") { return Status(ErrorCodes::BadValue, "use of shard replica set with name 'config' is not allowed"); } return shardName; }