Status LegacyReplicationCoordinator::processReplSetInitiate(OperationContext* txn, const BSONObj& givenConfig, BSONObjBuilder* resultObj) { log() << "replSet replSetInitiate admin command received from client" << rsLog; if (!_settings.usingReplSets()) { return Status(ErrorCodes::NoReplicationEnabled, "server is not running with --replSet"); } if( theReplSet ) { resultObj->append("info", "try querying " + rsConfigNs + " to see current configuration"); return Status(ErrorCodes::AlreadyInitialized, "already initialized"); } try { { // just make sure we can get a write lock before doing anything else. we'll // reacquire one later. of course it could be stuck then, but this check lowers the // risk if weird things are up. time_t t = time(0); Lock::GlobalWrite lk(txn->lockState()); if( time(0)-t > 10 ) { return Status(ErrorCodes::ExceededTimeLimit, "took a long time to get write lock, so not initiating. " "Initiate when server less busy?"); } /* check that we don't already have an oplog. that could cause issues. it is ok if the initiating member has *other* data than that. */ BSONObj o; if( Helpers::getFirst(txn, rsoplog, o) ) { return Status(ErrorCodes::AlreadyInitialized, rsoplog + string(" is not empty on the initiating member. " "cannot initiate.")); } } if( ReplSet::startupStatus == ReplSet::BADCONFIG ) { resultObj->append("info", ReplSet::startupStatusMsg.get()); return Status(ErrorCodes::InvalidReplicaSetConfig, "server already in BADCONFIG state (check logs); not initiating"); } if( ReplSet::startupStatus != ReplSet::EMPTYCONFIG ) { resultObj->append("startupStatus", ReplSet::startupStatus); resultObj->append("info", _settings.replSet); return Status(ErrorCodes::InvalidReplicaSetConfig, "all members and seeds must be reachable to initiate set"); } BSONObj configObj; if (!givenConfig.isEmpty()) { configObj = givenConfig; } else { resultObj->append("info2", "no configuration explicitly specified -- making one"); log() << "replSet info initiate : no configuration specified. " "Using a default configuration for the set" << rsLog; string name; vector<HostAndPort> seeds; set<HostAndPort> seedSet; parseReplSetSeedList(_settings.replSet, name, seeds, seedSet); // may throw... BSONObjBuilder b; b.append("_id", name); BSONObjBuilder members; HostAndPort me = someHostAndPortForMe(); members.append("0", BSON( "_id" << 0 << "host" << me.toString() )); resultObj->append("me", me.toString()); for( unsigned i = 0; i < seeds.size(); i++ ) { members.append(BSONObjBuilder::numStr(i+1), BSON( "_id" << i+1 << "host" << seeds[i].toString())); } b.appendArray("members", members.obj()); configObj = b.obj(); log() << "replSet created this configuration for initiation : " << configObj.toString() << rsLog; } scoped_ptr<ReplSetConfig> newConfig; try { newConfig.reset(ReplSetConfig::make(configObj)); } catch (const DBException& e) { log() << "replSet replSetInitiate exception: " << e.what() << rsLog; return Status(ErrorCodes::InvalidReplicaSetConfig, mongoutils::str::stream() << "couldn't parse cfg object " << e.what()); } if( newConfig->version > 1 ) { return Status(ErrorCodes::InvalidReplicaSetConfig, "can't initiate with a version number greater than 1"); } log() << "replSet replSetInitiate config object parses ok, " << newConfig->members.size() << " members specified" << rsLog; checkMembersUpForConfigChange(*newConfig, *resultObj, true); log() << "replSet replSetInitiate all members seem up" << rsLog; createOplog(txn); Lock::GlobalWrite lk(txn->lockState()); BSONObj comment = BSON( "msg" << "initiating set"); newConfig->saveConfigLocally(txn, comment); log() << "replSet replSetInitiate config now saved locally. " "Should come online in about a minute." << rsLog; resultObj->append("info", "Config now saved locally. Should come online in about a minute."); ReplSet::startupStatus = ReplSet::SOON; ReplSet::startupStatusMsg.set("Received replSetInitiate - " "should come online shortly."); } catch(const DBException& e ) { return e.toStatus(); } return Status::OK(); }
virtual bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) { log() << "replSet replSetInitiate admin command received from client" << rsLog; if( !replSet ) { errmsg = "server is not running with --replSet"; return false; } if( theReplSet ) { errmsg = "already initialized"; result.append("info", "try querying " + rsConfigNs + " to see current configuration"); return false; } { // just make sure we can get a write lock before doing anything else. we'll reacquire one // later. of course it could be stuck then, but this check lowers the risk if weird things // are up. time_t t = time(0); writelock lk(""); if( time(0)-t > 10 ) { errmsg = "took a long time to get write lock, so not initiating. Initiate when server less busy?"; return false; } /* check that we don't already have an oplog. that could cause issues. it is ok if the initiating member has *other* data than that. */ BSONObj o; if( Helpers::getFirst(rsoplog, o) ) { errmsg = rsoplog + string(" is not empty on the initiating member. cannot initiate."); return false; } } if( ReplSet::startupStatus == ReplSet::BADCONFIG ) { errmsg = "server already in BADCONFIG state (check logs); not initiating"; result.append("info", ReplSet::startupStatusMsg.get()); return false; } if( ReplSet::startupStatus != ReplSet::EMPTYCONFIG ) { result.append("startupStatus", ReplSet::startupStatus); errmsg = "all members and seeds must be reachable to initiate set"; result.append("info", cmdLine._replSet); return false; } BSONObj configObj; if( cmdObj["replSetInitiate"].type() != Object ) { result.append("info2", "no configuration explicitly specified -- making one"); log() << "replSet info initiate : no configuration specified. Using a default configuration for the set" << rsLog; string name; vector<HostAndPort> seeds; set<HostAndPort> seedSet; parseReplsetCmdLine(cmdLine._replSet, name, seeds, seedSet); // may throw... bob b; b.append("_id", name); bob members; members.append("0", BSON( "_id" << 0 << "host" << HostAndPort::Me().toString() )); for( unsigned i = 0; i < seeds.size(); i++ ) members.append(bob::numStr(i+1), BSON( "_id" << i+1 << "host" << seeds[i].toString())); b.appendArray("members", members.obj()); configObj = b.obj(); log() << "replSet created this configuration for initiation : " << configObj.toString() << rsLog; } else { configObj = cmdObj["replSetInitiate"].Obj(); } bool parsed = false; try { ReplSetConfig newConfig(configObj); parsed = true; if( newConfig.version > 1 ) { errmsg = "can't initiate with a version number greater than 1"; return false; } log() << "replSet replSetInitiate config object parses ok, " << newConfig.members.size() << " members specified" << rsLog; checkMembersUpForConfigChange(newConfig, true); log() << "replSet replSetInitiate all members seem up" << rsLog; createOplog(); writelock lk(""); bo comment = BSON( "msg" << "initiating set"); newConfig.saveConfigLocally(comment); log() << "replSet replSetInitiate config now saved locally. Should come online in about a minute." << rsLog; result.append("info", "Config now saved locally. Should come online in about a minute."); ReplSet::startupStatus = ReplSet::SOON; ReplSet::startupStatusMsg.set("Received replSetInitiate - should come online shortly."); } catch( DBException& e ) { log() << "replSet replSetInitiate exception: " << e.what() << rsLog; if( !parsed ) errmsg = string("couldn't parse cfg object ") + e.what(); else errmsg = string("couldn't initiate : ") + e.what(); return false; } return true; }
Status LegacyReplicationCoordinator::processReplSetReconfig(OperationContext* txn, const ReplSetReconfigArgs& args, BSONObjBuilder* resultObj) { if( args.force && !theReplSet ) { _settings.reconfig = args.newConfigObj.getOwned(); resultObj->append("msg", "will try this config momentarily, try running rs.conf() again in a " "few seconds"); return Status::OK(); } // TODO(dannenberg) once reconfig processing has been figured out in the impl, this should // be moved out of processReplSetReconfig and into the command body like all other cmds Status status = checkReplEnabledForCommand(resultObj); if (!status.isOK()) { return status; } if( !args.force && !theReplSet->box.getState().primary() ) { return Status(ErrorCodes::NotMaster, "replSetReconfig command must be sent to the current replica set " "primary."); } try { { // just make sure we can get a write lock before doing anything else. we'll // reacquire one later. of course it could be stuck then, but this check lowers the // risk if weird things are up - we probably don't want a change to apply 30 minutes // after the initial attempt. time_t t = time(0); Lock::GlobalWrite lk(txn->lockState()); if( time(0)-t > 20 ) { return Status(ErrorCodes::ExceededTimeLimit, "took a long time to get write lock, so not initiating. " "Initiate when server less busy?"); } } scoped_ptr<ReplSetConfig> newConfig(ReplSetConfig::make(args.newConfigObj, args.force)); log() << "replSet replSetReconfig config object parses ok, " << newConfig->members.size() << " members specified" << rsLog; Status status = ReplSetConfig::legalChange(theReplSet->getConfig(), *newConfig); if (!status.isOK()) { return status; } checkMembersUpForConfigChange(*newConfig, *resultObj, false); log() << "replSet replSetReconfig [2]" << rsLog; theReplSet->haveNewConfig(txn, *newConfig, true); ReplSet::startupStatusMsg.set("replSetReconfig'd"); } catch(const DBException& e) { log() << "replSet replSetReconfig exception: " << e.what() << rsLog; return e.toStatus(); } resetSlaveCache(); return Status::OK(); }