/**
* Copyright (C) 2014 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kReplication
#include "mongo/platform/basic.h"
#include "mongo/db/repl/is_master_response.h"
#include "mongo/base/status.h"
#include "mongo/bson/oid.h"
#include "mongo/bson/util/bson_extract.h"
#include "mongo/db/jsobj.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
namespace repl {
namespace {
const std::string kIsMasterFieldName = "ismaster";
const std::string kSecondaryFieldName = "secondary";
const std::string kSetNameFieldName = "setName";
const std::string kSetVersionFieldName = "setVersion";
const std::string kHostsFieldName = "hosts";
const std::string kPassivesFieldName = "passives";
const std::string kArbitersFieldName = "arbiters";
const std::string kPrimaryFieldName = "primary";
const std::string kArbiterOnlyFieldName = "arbiterOnly";
const std::string kPassiveFieldName = "passive";
const std::string kHiddenFieldName = "hidden";
const std::string kBuildIndexesFieldName = "buildIndexes";
const std::string kSlaveDelayFieldName = "slaveDelay";
const std::string kTagsFieldName = "tags";
const std::string kMeFieldName = "me";
const std::string kElectionIdFieldName = "electionId";
const std::string kLastWriteOpTimeFieldName = "opTime";
const std::string kLastWriteDateFieldName = "lastWriteDate";
const std::string kLastMajorityWriteOpTimeFieldName = "majorityOpTime";
const std::string kLastMajorityWriteDateFieldName = "majorityWriteDate";
const std::string kLastWriteFieldName = "lastWrite";
// field name constants that don't directly correspond to member variables
const std::string kInfoFieldName = "info";
const std::string kIsReplicaSetFieldName = "isreplicaset";
const std::string kErrmsgFieldName = "errmsg";
const std::string kCodeFieldName = "code";
} // namespace
IsMasterResponse::IsMasterResponse()
: _isMaster(false),
_isMasterSet(false),
_secondary(false),
_isSecondarySet(false),
_setNameSet(false),
_setVersion(0),
_setVersionSet(false),
_hostsSet(false),
_passivesSet(false),
_arbitersSet(false),
_primarySet(false),
_arbiterOnly(false),
_arbiterOnlySet(false),
_passive(false),
_passiveSet(false),
_hidden(false),
_hiddenSet(false),
_buildIndexes(true),
_buildIndexesSet(false),
_slaveDelay(0),
_slaveDelaySet(false),
_tagsSet(false),
_meSet(false),
_electionId(OID()),
_configSet(true),
_shutdownInProgress(false) {}
void IsMasterResponse::addToBSON(BSONObjBuilder* builder) const {
if (_hostsSet) {
std::vector hosts;
for (size_t i = 0; i < _hosts.size(); ++i) {
hosts.push_back(_hosts[i].toString());
}
builder->append(kHostsFieldName, hosts);
}
if (_passivesSet) {
std::vector passives;
for (size_t i = 0; i < _passives.size(); ++i) {
passives.push_back(_passives[i].toString());
}
builder->append(kPassivesFieldName, passives);
}
if (_arbitersSet) {
std::vector arbiters;
for (size_t i = 0; i < _arbiters.size(); ++i) {
arbiters.push_back(_arbiters[i].toString());
}
builder->append(kArbitersFieldName, arbiters);
}
if (_setNameSet) {
builder->append(kSetNameFieldName, _setName);
}
if (_shutdownInProgress) {
builder->append(kCodeFieldName, ErrorCodes::ShutdownInProgress);
builder->append(kErrmsgFieldName, "replication shutdown in progress");
return;
}
if (!_configSet) {
builder->append(kIsMasterFieldName, false);
builder->append(kSecondaryFieldName, false);
builder->append(kInfoFieldName, "Does not have a valid replica set config");
builder->append(kIsReplicaSetFieldName, true);
return;
}
invariant(_setVersionSet);
builder->append(kSetVersionFieldName, static_cast(_setVersion));
invariant(_isMasterSet);
builder->append(kIsMasterFieldName, _isMaster);
invariant(_isSecondarySet);
builder->append(kSecondaryFieldName, _secondary);
if (_primarySet)
builder->append(kPrimaryFieldName, _primary.toString());
if (_arbiterOnlySet)
builder->append(kArbiterOnlyFieldName, _arbiterOnly);
if (_passiveSet)
builder->append(kPassiveFieldName, _passive);
if (_hiddenSet)
builder->append(kHiddenFieldName, _hidden);
if (_buildIndexesSet)
builder->append(kBuildIndexesFieldName, _buildIndexes);
if (_slaveDelaySet)
builder->appendIntOrLL(kSlaveDelayFieldName, durationCount(_slaveDelay));
if (_tagsSet) {
BSONObjBuilder tags(builder->subobjStart(kTagsFieldName));
for (unordered_map::const_iterator it = _tags.begin();
it != _tags.end();
++it) {
tags.append(it->first, it->second);
}
}
invariant(_meSet);
builder->append(kMeFieldName, _me.toString());
if (_electionId.isSet())
builder->append(kElectionIdFieldName, _electionId);
if (_lastWrite || _lastMajorityWrite) {
BSONObjBuilder lastWrite(builder->subobjStart(kLastWriteFieldName));
if (_lastWrite) {
lastWrite.append(kLastWriteOpTimeFieldName, _lastWrite->opTime.toBSON());
lastWrite.appendTimeT(kLastWriteDateFieldName, _lastWrite->value);
}
if (_lastMajorityWrite) {
lastWrite.append(kLastMajorityWriteOpTimeFieldName,
_lastMajorityWrite->opTime.toBSON());
lastWrite.appendTimeT(kLastMajorityWriteDateFieldName, _lastMajorityWrite->value);
}
}
}
BSONObj IsMasterResponse::toBSON() const {
BSONObjBuilder builder;
addToBSON(&builder);
return builder.obj();
}
Status IsMasterResponse::initialize(const BSONObj& doc) {
Status status = bsonExtractBooleanField(doc, kIsMasterFieldName, &_isMaster);
if (!status.isOK()) {
return status;
}
_isMasterSet = true;
status = bsonExtractBooleanField(doc, kSecondaryFieldName, &_secondary);
if (!status.isOK()) {
return status;
}
_isSecondarySet = true;
if (doc.hasField(kInfoFieldName)) {
if (_isMaster || _secondary || !doc.hasField(kIsReplicaSetFieldName) ||
!doc[kIsReplicaSetFieldName].booleanSafe()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "Expected presence of \"" << kInfoFieldName
<< "\" field to indicate no valid config loaded, but other "
"fields weren't as we expected");
}
_configSet = false;
return Status::OK();
} else {
if (doc.hasField(kIsReplicaSetFieldName)) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "Found \"" << kIsReplicaSetFieldName
<< "\" field which should indicate that no valid config "
"is loaded, but we didn't also have an \""
<< kInfoFieldName
<< "\" field as we expected");
}
}
status = bsonExtractStringField(doc, kSetNameFieldName, &_setName);
if (!status.isOK()) {
return status;
}
_setNameSet = true;
status = bsonExtractIntegerField(doc, kSetVersionFieldName, &_setVersion);
if (!status.isOK()) {
return status;
}
_setVersionSet = true;
if (doc.hasField(kHostsFieldName)) {
BSONElement hostsElement;
status = bsonExtractTypedField(doc, kHostsFieldName, Array, &hostsElement);
if (!status.isOK()) {
return status;
}
for (BSONObjIterator it(hostsElement.Obj()); it.more();) {
BSONElement hostElement = it.next();
if (hostElement.type() != String) {
return Status(ErrorCodes::TypeMismatch,
str::stream() << "Elements in \"" << kHostsFieldName
<< "\" array of isMaster response must be of type "
<< typeName(String)
<< " but found type "
<< typeName(hostElement.type()));
}
_hosts.push_back(HostAndPort(hostElement.String()));
}
_hostsSet = true;
}
if (doc.hasField(kPassivesFieldName)) {
BSONElement passivesElement;
status = bsonExtractTypedField(doc, kPassivesFieldName, Array, &passivesElement);
if (!status.isOK()) {
return status;
}
for (BSONObjIterator it(passivesElement.Obj()); it.more();) {
BSONElement passiveElement = it.next();
if (passiveElement.type() != String) {
return Status(ErrorCodes::TypeMismatch,
str::stream() << "Elements in \"" << kPassivesFieldName
<< "\" array of isMaster response must be of type "
<< typeName(String)
<< " but found type "
<< typeName(passiveElement.type()));
}
_passives.push_back(HostAndPort(passiveElement.String()));
}
_passivesSet = true;
}
if (doc.hasField(kArbitersFieldName)) {
BSONElement arbitersElement;
status = bsonExtractTypedField(doc, kArbitersFieldName, Array, &arbitersElement);
if (!status.isOK()) {
return status;
}
for (BSONObjIterator it(arbitersElement.Obj()); it.more();) {
BSONElement arbiterElement = it.next();
if (arbiterElement.type() != String) {
return Status(ErrorCodes::TypeMismatch,
str::stream() << "Elements in \"" << kArbitersFieldName
<< "\" array of isMaster response must be of type "
<< typeName(String)
<< " but found type "
<< typeName(arbiterElement.type()));
}
_arbiters.push_back(HostAndPort(arbiterElement.String()));
}
_arbitersSet = true;
}
if (doc.hasField(kPrimaryFieldName)) {
std::string primaryString;
status = bsonExtractStringField(doc, kPrimaryFieldName, &primaryString);
if (!status.isOK()) {
return status;
}
_primary = HostAndPort(primaryString);
_primarySet = true;
}
if (doc.hasField(kArbiterOnlyFieldName)) {
status = bsonExtractBooleanField(doc, kArbiterOnlyFieldName, &_arbiterOnly);
if (!status.isOK()) {
return status;
}
_arbiterOnlySet = true;
}
if (doc.hasField(kPassiveFieldName)) {
status = bsonExtractBooleanField(doc, kPassiveFieldName, &_passive);
if (!status.isOK()) {
return status;
}
_passiveSet = true;
}
if (doc.hasField(kHiddenFieldName)) {
status = bsonExtractBooleanField(doc, kHiddenFieldName, &_hidden);
if (!status.isOK()) {
return status;
}
_hiddenSet = true;
}
if (doc.hasField(kBuildIndexesFieldName)) {
status = bsonExtractBooleanField(doc, kBuildIndexesFieldName, &_buildIndexes);
if (!status.isOK()) {
return status;
}
_buildIndexesSet = true;
}
if (doc.hasField(kSlaveDelayFieldName)) {
long long slaveDelaySecs;
status = bsonExtractIntegerField(doc, kSlaveDelayFieldName, &slaveDelaySecs);
if (!status.isOK()) {
return status;
}
_slaveDelaySet = true;
_slaveDelay = Seconds(slaveDelaySecs);
}
if (doc.hasField(kTagsFieldName)) {
BSONElement tagsElement;
status = bsonExtractTypedField(doc, kTagsFieldName, Object, &tagsElement);
if (!status.isOK()) {
return status;
}
for (BSONObjIterator it(tagsElement.Obj()); it.more();) {
BSONElement tagElement = it.next();
if (tagElement.type() != String) {
return Status(ErrorCodes::TypeMismatch,
str::stream() << "Elements in \"" << kTagsFieldName
<< "\" obj "
"of isMaster response must be of type "
<< typeName(String)
<< " but found type "
<< typeName(tagsElement.type()));
}
_tags[tagElement.fieldNameStringData().toString()] = tagElement.String();
}
_tagsSet = true;
}
if (doc.hasField(kElectionIdFieldName)) {
BSONElement electionIdElem;
status = bsonExtractTypedField(doc, kElectionIdFieldName, jstOID, &electionIdElem);
if (!status.isOK()) {
return status;
}
_electionId = electionIdElem.OID();
}
if (doc.hasField(kLastWriteFieldName)) {
BSONElement lastWriteElement;
status = bsonExtractTypedField(doc, kLastWriteFieldName, Object, &lastWriteElement);
if (!status.isOK()) {
return status;
}
BSONObj lastWriteObj = lastWriteElement.Obj();
bool lastWriteOpTimeSet = false;
bool lastWriteDateSet = false;
if (auto lastWriteOpTimeElement = lastWriteObj[kLastWriteOpTimeFieldName]) {
if (lastWriteOpTimeElement.type() != Object) {
return Status(ErrorCodes::TypeMismatch,
str::stream() << "Elements in \"" << kLastWriteOpTimeFieldName
<< "\" obj "
"of isMaster response must be of type "
<< typeName(Object)
<< " but found type "
<< typeName(lastWriteOpTimeElement.type()));
}
auto lastWriteOpTime = OpTime::parseFromOplogEntry(lastWriteOpTimeElement.Obj());
if (!lastWriteOpTime.isOK()) {
return lastWriteOpTime.getStatus();
}
if (_lastWrite) {
_lastWrite->opTime = lastWriteOpTime.getValue();
} else {
_lastWrite = OpTimeWith(0, lastWriteOpTime.getValue());
}
lastWriteOpTimeSet = true;
}
if (auto lastWriteDateElement = lastWriteObj[kLastWriteDateFieldName]) {
if (lastWriteDateElement.type() != Date) {
return Status(ErrorCodes::TypeMismatch,
str::stream() << "Elements in \"" << kLastWriteDateFieldName
<< "\" obj "
"of isMaster response must be of type "
<< typeName(Date)
<< " but found type "
<< typeName(lastWriteDateElement.type()));
}
if (_lastWrite) {
_lastWrite->value = lastWriteDateElement.Date().toTimeT();
} else {
_lastWrite = OpTimeWith(lastWriteDateElement.Date().toTimeT(), OpTime());
}
lastWriteDateSet = true;
}
invariant(lastWriteOpTimeSet == lastWriteDateSet);
bool lastMajorityWriteOpTimeSet = false;
bool lastMajorityWriteDateSet = false;
if (auto lastMajorityWriteOpTimeElement = lastWriteObj[kLastMajorityWriteOpTimeFieldName]) {
if (lastMajorityWriteOpTimeElement.type() != Object) {
return Status(ErrorCodes::TypeMismatch,
str::stream() << "Elements in \"" << kLastMajorityWriteOpTimeFieldName
<< "\" obj "
"of isMaster response must be of type "
<< typeName(Object)
<< " but found type "
<< typeName(lastMajorityWriteOpTimeElement.type()));
}
auto lastMajorityWriteOpTime =
OpTime::parseFromOplogEntry(lastMajorityWriteOpTimeElement.Obj());
if (!lastMajorityWriteOpTime.isOK()) {
return lastMajorityWriteOpTime.getStatus();
}
if (_lastMajorityWrite) {
_lastMajorityWrite->opTime = lastMajorityWriteOpTime.getValue();
} else {
_lastMajorityWrite = OpTimeWith(0, lastMajorityWriteOpTime.getValue());
}
lastMajorityWriteOpTimeSet = true;
}
if (auto lastMajorityWriteDateElement = lastWriteObj[kLastMajorityWriteDateFieldName]) {
if (lastMajorityWriteDateElement.type() != Date) {
return Status(ErrorCodes::TypeMismatch,
str::stream() << "Elements in \"" << kLastMajorityWriteDateFieldName
<< "\" obj "
"of isMaster response must be of type "
<< typeName(Date)
<< " but found type "
<< typeName(lastMajorityWriteDateElement.type()));
}
if (_lastMajorityWrite) {
_lastMajorityWrite->value = lastMajorityWriteDateElement.Date().toTimeT();
} else {
_lastMajorityWrite =
OpTimeWith(lastMajorityWriteDateElement.Date().toTimeT(), OpTime());
}
lastMajorityWriteDateSet = true;
}
invariant(lastMajorityWriteOpTimeSet == lastMajorityWriteDateSet);
}
std::string meString;
status = bsonExtractStringField(doc, kMeFieldName, &meString);
if (!status.isOK()) {
return status;
}
_me = HostAndPort(meString);
_meSet = true;
return Status::OK();
}
void IsMasterResponse::setIsMaster(bool isMaster) {
_isMasterSet = true;
_isMaster = isMaster;
}
void IsMasterResponse::setIsSecondary(bool secondary) {
_isSecondarySet = true;
_secondary = secondary;
}
void IsMasterResponse::setReplSetName(const std::string& setName) {
_setNameSet = true;
_setName = setName;
}
void IsMasterResponse::setReplSetVersion(long long version) {
_setVersionSet = true;
_setVersion = version;
}
void IsMasterResponse::addHost(const HostAndPort& host) {
_hostsSet = true;
_hosts.push_back(host);
}
void IsMasterResponse::addPassive(const HostAndPort& passive) {
_passivesSet = true;
_passives.push_back(passive);
}
void IsMasterResponse::addArbiter(const HostAndPort& arbiter) {
_arbitersSet = true;
_arbiters.push_back(arbiter);
}
void IsMasterResponse::setPrimary(const HostAndPort& primary) {
_primarySet = true;
_primary = primary;
}
void IsMasterResponse::setIsArbiterOnly(bool arbiterOnly) {
_arbiterOnlySet = true;
_arbiterOnly = arbiterOnly;
}
void IsMasterResponse::setIsPassive(bool passive) {
_passiveSet = true;
_passive = passive;
}
void IsMasterResponse::setIsHidden(bool hidden) {
_hiddenSet = true;
_hidden = hidden;
}
void IsMasterResponse::setShouldBuildIndexes(bool buildIndexes) {
_buildIndexesSet = true;
_buildIndexes = buildIndexes;
}
void IsMasterResponse::setSlaveDelay(Seconds slaveDelay) {
_slaveDelaySet = true;
_slaveDelay = slaveDelay;
}
void IsMasterResponse::addTag(const std::string& tagKey, const std::string& tagValue) {
_tagsSet = true;
_tags[tagKey] = tagValue;
}
void IsMasterResponse::setMe(const HostAndPort& me) {
_meSet = true;
_me = me;
}
void IsMasterResponse::setElectionId(const OID& electionId) {
_electionId = electionId;
}
void IsMasterResponse::setLastWrite(const OpTime& lastWriteOpTime, const time_t lastWriteDate) {
_lastWrite = OpTimeWith(lastWriteDate, lastWriteOpTime);
}
void IsMasterResponse::setLastMajorityWrite(const OpTime& lastMajorityWriteOpTime,
const time_t lastMajorityWriteDate) {
_lastMajorityWrite = OpTimeWith(lastMajorityWriteDate, lastMajorityWriteOpTime);
}
void IsMasterResponse::markAsNoConfig() {
_configSet = false;
}
void IsMasterResponse::markAsShutdownInProgress() {
_shutdownInProgress = true;
}
} // namespace repl
} // namespace mongo