/* Copyright 2012 10gen 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.
*/
#include "mongo/dbtests/mock/mock_replica_set.h"
#include "mongo/db/repl/member_state.h"
#include "mongo/dbtests/mock/mock_conn_registry.h"
#include "mongo/dbtests/mock/mock_dbclient_connection.h"
#include "mongo/util/map_util.h"
#include
namespace mongo {
MockReplicaSet::MockReplicaSet(const string& setName, size_t nodes):
_setName(setName) {
ReplConfigMap replConfig;
for (size_t n = 0; n < nodes; n++) {
std::stringstream str;
str << "$" << setName << n << ":27017";
const string hostName(str.str());
if (n == 0) {
_primaryHost = hostName;
}
MockRemoteDBServer* mockServer = new MockRemoteDBServer(hostName);
_nodeMap[hostName] = mockServer;
MockConnRegistry::get()->addServer(mockServer);
ReplSetConfig::MemberCfg config;
config.h = HostAndPort(hostName);
replConfig.insert(std::make_pair(hostName, config));
}
setConfig(replConfig);
}
MockReplicaSet::~MockReplicaSet() {
for (ReplNodeMap::iterator iter = _nodeMap.begin();
iter != _nodeMap.end(); ++iter) {
MockConnRegistry::get()->removeServer(iter->second->getServerAddress());
delete iter->second;
}
}
string MockReplicaSet::getSetName() const {
return _setName;
}
string MockReplicaSet::getConnectionString() const {
std::stringstream str;
str << _setName;
str << "/";
ReplNodeMap::const_iterator iter = _nodeMap.begin();
while (iter != _nodeMap.end()) {
str << iter->second->getServerAddress();
++iter;
if (iter != _nodeMap.end()) {
str << ",";
}
}
return str.str();
}
vector MockReplicaSet::getHosts() const {
vector list;
for (ReplNodeMap::const_iterator iter = _nodeMap.begin();
iter != _nodeMap.end(); ++iter) {
list.push_back(HostAndPort(iter->second->getServerAddress()));
}
return list;
}
string MockReplicaSet::getPrimary() const {
return _primaryHost;
}
void MockReplicaSet::setPrimary(const string& hostAndPort) {
ReplConfigMap::const_iterator iter = _replConfig.find(hostAndPort);
fassert(16578, iter != _replConfig.end());
const ReplSetConfig::MemberCfg& config = iter->second;
fassert(16579, !config.hidden && config.priority > 0 && !config.arbiterOnly);
_primaryHost = hostAndPort;
mockIsMasterCmd();
mockReplSetGetStatusCmd();
}
vector MockReplicaSet::getSecondaries() const {
vector secondaries;
for (ReplConfigMap::const_iterator iter = _replConfig.begin();
iter != _replConfig.end(); ++iter) {
if (iter->first != _primaryHost) {
secondaries.push_back(iter->first);
}
}
return secondaries;
}
MockRemoteDBServer* MockReplicaSet::getNode(const string& hostAndPort) {
return mapFindWithDefault(_nodeMap, hostAndPort, static_cast(NULL));
}
MockReplicaSet::ReplConfigMap MockReplicaSet::getReplConfig() const {
return _replConfig;
}
void MockReplicaSet::setConfig(const MockReplicaSet::ReplConfigMap& newConfig) {
_replConfig = newConfig;
mockIsMasterCmd();
mockReplSetGetStatusCmd();
}
void MockReplicaSet::kill(const string& hostAndPort) {
verify(_nodeMap.count(hostAndPort) == 1);
_nodeMap[hostAndPort]->shutdown();
}
void MockReplicaSet::kill(const vector& hostList) {
for (vector::const_iterator iter = hostList.begin();
iter != hostList.end(); ++iter) {
kill(*iter);
}
}
void MockReplicaSet::restore(const string& hostAndPort) {
verify(_nodeMap.count(hostAndPort) == 1);
_nodeMap[hostAndPort]->reboot();
}
void MockReplicaSet::mockIsMasterCmd() {
// Copied from ReplSetImpl::_fillIsMaster
for (ReplNodeMap::iterator nodeIter = _nodeMap.begin();
nodeIter != _nodeMap.end(); ++nodeIter) {
const string& hostAndPort = nodeIter->first;
BSONObjBuilder builder;
builder.append("setName", _setName);
ReplConfigMap::const_iterator configIter = _replConfig.find(hostAndPort);
if (configIter == _replConfig.end()) {
builder.append("ismaster", false);
builder.append("secondary", false);
vector hostList;
builder.append("hosts", hostList);
}
else {
const bool isPrimary = hostAndPort == getPrimary();
builder.append("ismaster", isPrimary);
builder.append("secondary", !isPrimary);
{
// TODO: add passives & arbiters
vector hostList;
hostList.push_back(getPrimary());
const vector secondaries = getSecondaries();
for (vector::const_iterator secIter = secondaries.begin();
secIter != secondaries.end(); ++secIter) {
hostList.push_back(*secIter);
}
builder.append("hosts", hostList);
}
builder.append("primary", getPrimary());
const ReplSetConfig::MemberCfg& replConfig = configIter->second;
if (replConfig.arbiterOnly) {
builder.append("arbiterOnly", true);
}
if (replConfig.priority == 0 && !replConfig.arbiterOnly) {
builder.append("passive", true);
}
if (replConfig.slaveDelay) {
builder.append("slaveDelay", replConfig.slaveDelay);
}
if (replConfig.hidden) {
builder.append("hidden", true);
}
if (!replConfig.buildIndexes) {
builder.append("buildIndexes", false);
}
if(!replConfig.tags.empty()) {
BSONObjBuilder tagBuilder;
for(map::const_iterator tagIter = replConfig.tags.begin();
tagIter != replConfig.tags.end(); tagIter++) {
tagBuilder.append(tagIter->first, tagIter->second);
}
builder.append("tags", tagBuilder.done());
}
}
builder.append("me", hostAndPort);
builder.append("ok", true);
nodeIter->second->setCommandReply("ismaster", builder.done());
}
}
int MockReplicaSet::getState(const std::string& hostAndPort) const {
if (_replConfig.count(hostAndPort) < 1) {
return static_cast(MemberState::RS_SHUNNED);
}
else if (hostAndPort == getPrimary()) {
return static_cast(MemberState::RS_PRIMARY);
}
else {
return static_cast(MemberState::RS_SECONDARY);
}
}
void MockReplicaSet::mockReplSetGetStatusCmd() {
// Copied from ReplSetImpl::_summarizeStatus
for (ReplNodeMap::iterator nodeIter = _nodeMap.begin();
nodeIter != _nodeMap.end(); ++nodeIter) {
MockRemoteDBServer* node = nodeIter->second;
vector hostsField;
BSONObjBuilder fullStatBuilder;
{
BSONObjBuilder selfStatBuilder;
selfStatBuilder.append("name", node->getServerAddress());
selfStatBuilder.append("health", 1.0);
selfStatBuilder.append("state", getState(node->getServerAddress()));
selfStatBuilder.append("self", true);
// TODO: _id, stateStr, uptime, optime, optimeDate, maintenanceMode, errmsg
hostsField.push_back(selfStatBuilder.obj());
}
for (ReplConfigMap::const_iterator replConfIter = _replConfig.begin();
replConfIter != _replConfig.end(); ++replConfIter) {
MockRemoteDBServer* hostNode = getNode(replConfIter->first);
if (hostNode == node) {
continue;
}
BSONObjBuilder hostMemberBuilder;
// TODO: _id, stateStr, uptime, optime, optimeDate, lastHeartbeat, pingMs
// errmsg, authenticated
hostMemberBuilder.append("name", hostNode->getServerAddress());
const double health = hostNode->isRunning() ? 1.0 : 0.0;
hostMemberBuilder.append("health", health);
hostMemberBuilder.append("state", getState(hostNode->getServerAddress()));
hostsField.push_back(hostMemberBuilder.obj());
}
sort(hostsField.begin(), hostsField.end());
// TODO: syncingTo
fullStatBuilder.append("set", _setName);
fullStatBuilder.appendTimeT("date", time(0));
fullStatBuilder.append("myState", getState(node->getServerAddress()));
fullStatBuilder.append("members", hostsField);
fullStatBuilder.append("ok", true);
node->setCommandReply("replSetGetStatus", fullStatBuilder.done());
}
}
}