0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 17:10:48 +01:00

SERVER-14165 port isSelf fastpath to windows

Closes #736

Signed-off-by: Benety Goh <benety@mongodb.com>
This commit is contained in:
Adam Midvidy 2014-07-31 16:42:21 -04:00 committed by Benety Goh
parent 0adbb451f7
commit daab6fae49
7 changed files with 496 additions and 235 deletions

View File

@ -842,7 +842,13 @@ elif windows:
# This gives 32-bit programs 4 GB of user address space in WOW64, ignored in 64-bit builds
env.Append( LINKFLAGS=["/LARGEADDRESSAWARE"] )
env.Append(LIBS=['ws2_32.lib', 'kernel32.lib', 'advapi32.lib', 'Psapi.lib', 'DbgHelp.lib', 'shell32.lib'])
env.Append(LIBS=['ws2_32.lib',
'kernel32.lib',
'advapi32.lib',
'Psapi.lib',
'DbgHelp.lib',
'shell32.lib',
'Iphlpapi.lib'])
# v8 calls timeGetTime()
if usev8:

View File

@ -444,6 +444,7 @@ coredbEnv.Library("coredb", [
"db/commands/find_and_modify_common.cpp",
"db/commands/hashcmd.cpp",
"db/commands/isself.cpp",
"db/repl/isself.cpp",
"db/commands/mr_common.cpp",
"db/commands/rename_collection_common.cpp",
"db/commands/server_status.cpp",

View File

@ -28,155 +28,17 @@
* it in the license file.
*/
#include "mongo/pch.h"
#include "mongo/platform/basic.h"
#include <boost/algorithm/string.hpp>
#include <string>
#include <vector>
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/security_key.h"
#include "mongo/base/init.h"
#include "mongo/db/commands.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/repl/isself.h"
#include "mongo/db/server_options.h"
#include "mongo/util/net/listen.h"
#include "mongo/util/net/hostandport.h"
#include "mongo/client/dbclientinterface.h"
#ifndef _WIN32
# ifndef __sunos__
# include <ifaddrs.h>
# endif
# include <sys/resource.h>
# include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#ifdef __openbsd__
# include <sys/uio.h>
#endif
#endif
namespace mongo {
#if !defined(_WIN32) && !defined(__sunos__)
static vector<string> getMyAddrs() {
vector<string> out;
ifaddrs * addrs;
if (!serverGlobalParams.bind_ip.empty()) {
boost::split(out, serverGlobalParams.bind_ip, boost::is_any_of(", "));
return out;
}
int status = getifaddrs(&addrs);
massert(13469, "getifaddrs failure: " + errnoWithDescription(errno), status == 0);
// based on example code from linux getifaddrs manpage
for (ifaddrs * addr = addrs; addr != NULL; addr = addr->ifa_next) {
if ( addr->ifa_addr == NULL ) continue;
int family = addr->ifa_addr->sa_family;
char host[NI_MAXHOST];
if (family == AF_INET || family == AF_INET6) {
status = getnameinfo(addr->ifa_addr,
(family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if ( status != 0 ) {
freeifaddrs( addrs );
addrs = NULL;
msgasserted( 13470, string("getnameinfo() failed: ") + gai_strerror(status) );
}
out.push_back(host);
}
}
freeifaddrs( addrs );
addrs = NULL;
if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) {
LogstreamBuilder builder(logger::globalLogDomain(),
getThreadName(),
logger::LogSeverity::Debug(1));
builder << "getMyAddrs():";
for (vector<string>::const_iterator it=out.begin(), end=out.end(); it!=end; ++it) {
builder << " [" << *it << ']';
}
builder << endl;
}
return out;
}
static vector<string> getAllIPs(const string& iporhost) {
addrinfo* addrs = NULL;
addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = (IPv6Enabled() ? AF_UNSPEC : AF_INET);
static string portNum = BSONObjBuilder::numStr(serverGlobalParams.port);
vector<string> out;
int ret = getaddrinfo(iporhost.c_str(), portNum.c_str(), &hints, &addrs);
if ( ret ) {
warning() << "getaddrinfo(\"" << iporhost << "\") failed: " << gai_strerror(ret) << endl;
return out;
}
for (addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
int family = addr->ai_family;
char host[NI_MAXHOST];
if (family == AF_INET || family == AF_INET6) {
int status = getnameinfo(addr->ai_addr, addr->ai_addrlen, host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
massert(13472, string("getnameinfo() failed: ") + gai_strerror(status), status == 0);
out.push_back(host);
}
}
freeaddrinfo(addrs);
if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) {
LogstreamBuilder builder(logger::globalLogDomain(),
getThreadName(),
logger::LogSeverity::Debug(1));
builder << "getallIPs(\"" << iporhost << "\"):";
for (vector<string>::const_iterator it=out.begin(), end=out.end(); it!=end; ++it) {
builder << " [" << *it << ']';
}
builder << endl;
}
return out;
}
#endif
class IsSelfCommand : public Command {
public:
IsSelfCommand() : Command("_isSelf") , _cacheLock( "IsSelfCommand::_cacheLock" ) {}
IsSelfCommand() : Command("_isSelf") {}
virtual bool slaveOk() const { return true; }
virtual bool isWriteCommandForConfigServer() const { return false; }
virtual void help( stringstream &help ) const {
@ -186,101 +48,16 @@ namespace mongo {
const BSONObj& cmdObj,
std::vector<Privilege>* out) {} // No auth required
bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
init();
result.append( "id" , _id );
result.append( "id" , repl::instanceId );
return true;
}
};
void init() {
scoped_lock lk( _cacheLock );
if ( ! _id.isSet() )
_id.init();
}
OID _id;
mongo::mutex _cacheLock;
map<string,bool> _cache;
} isSelfCommand;
bool repl::isSelf(const HostAndPort& hostAndPort) {
int p = hostAndPort.port();
string host = str::stream() << hostAndPort.host() << ":" << p;
{
// check cache for this host
// debatably something _could_ change, but I'm not sure right now (erh 10/14/2010)
scoped_lock lk( isSelfCommand._cacheLock );
map<string,bool>::const_iterator i = isSelfCommand._cache.find( host );
if ( i != isSelfCommand._cache.end() )
return i->second;
}
#if !defined(_WIN32) && !defined(__sunos__)
// on linux and os x we can do a quick check for an ip match
// no need for ip match if the ports do not match
if (p == serverGlobalParams.port) {
const vector<string> myaddrs = getMyAddrs();
const vector<string> addrs = getAllIPs(hostAndPort.host());
for (vector<string>::const_iterator i=myaddrs.begin(), iend=myaddrs.end();
i!=iend; ++i) {
for (vector<string>::const_iterator j=addrs.begin(), jend=addrs.end();
j!=jend; ++j) {
string a = *i;
string b = *j;
if (a == b) {
// add to cache
scoped_lock lk( isSelfCommand._cacheLock );
isSelfCommand._cache[host] = true;
return true;
}
}
}
}
#endif
if ( ! Listener::getTimeTracker() ) {
// this ensures we are actually running a server
// this may return true later, so may want to retry
return false;
}
try {
isSelfCommand.init();
DBClientConnection conn;
string errmsg;
if ( ! conn.connect( hostAndPort , errmsg ) ) {
// should this go in the cache?
return false;
}
if (getGlobalAuthorizationManager()->isAuthEnabled() && isInternalAuthSet()) {
if (!authenticateInternalUser(&conn)) {
return false;
}
}
BSONObj out;
bool ok = conn.simpleCommand( "admin" , &out , "_isSelf" );
bool me = ok && out["id"].type() == jstOID && isSelfCommand._id == out["id"].OID();
// add to cache
scoped_lock lk( isSelfCommand._cacheLock );
isSelfCommand._cache[host] = me;
return me;
}
catch ( std::exception& e ) {
warning() << "could't check isSelf (" << host << ") " << e.what() << endl;
}
return false;
MONGO_INITIALIZER_WITH_PREREQUISITES(RegisterIsSelfCommand, ("GenerateInstanceId"))
(InitializerContext* context) {
// Leaked intentionally: a Command registers itself when constructed
new IsSelfCommand();
return Status::OK();
}
}

View File

@ -118,3 +118,14 @@ env.CppUnitTest('replica_set_config_test',
'replica_set_tag_test.cpp',
],
LIBDEPS=['replica_set_messages'])
env.CppUnitTest('isself_test',
[
'isself_test.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/serveronly',
'$BUILD_DIR/mongo/coreserver',
'$BUILD_DIR/mongo/coredb',
],
NO_CRUTCH = True)

View File

@ -0,0 +1,359 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*
* 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/platform/basic.h"
#include "mongo/db/repl/isself.h"
#include <boost/algorithm/string.hpp>
#include "mongo/base/init.h"
#include "mongo/client/dbclientinterface.h"
#include "mongo/db/commands.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/security_key.h"
#include "mongo/util/scopeguard.h"
#include "mongo/util/log.h"
#ifdef __linux__
#include <ifaddrs.h>
#include <netdb.h>
#elif defined(_WIN32)
#include <boost/asio/detail/socket_ops.hpp>
#include <boost/scoped_array.hpp>
#include <boost/system/error_code.hpp>
#include <iphlpapi.h>
#include <winsock2.h>
#include <Ws2tcpip.h>
#endif // defined(_WIN32)
#if defined(_WIN32) || defined(__linux__)
#define FASTPATH 1
#endif
namespace mongo {
namespace repl {
OID instanceId;
MONGO_INITIALIZER(GenerateInstanceId)(InitializerContext*) {
instanceId = OID::gen();
return Status::OK();
}
#ifdef FASTPATH
namespace {
/**
* Helper to convert a message from a networking function to a string.
* Needed because errnoWithDescription uses strerror on linux, when
* we need gai_strerror.
*/
std::string stringifyError(int code) {
#ifdef __linux__
return gai_strerror(code);
#elif defined(_WIN32)
// FormatMessage in errnoWithDescription works here on windows
return errnoWithDescription(code);
#endif
}
/**
* Resolves a host and port to a list of IP addresses. This requires a syscall. If the
* ipv6enabled parameter is true, both IPv6 and IPv4 addresses will be returned.
*/
std::vector<std::string> getAddrsForHost(const std::string& iporhost,
const int port,
const bool ipv6enabled) {
addrinfo* addrs = NULL;
addrinfo hints = {0};
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = (ipv6enabled ? AF_UNSPEC : AF_INET);
const std::string portNum = BSONObjBuilder::numStr(port);
std::vector<std::string> out;
int err = getaddrinfo(iporhost.c_str(), portNum.c_str(), &hints, &addrs);
if (err) {
warning() << "getaddrinfo(\"" << iporhost << "\") failed: "
<< stringifyError(err) << std::endl;
return out;
}
ON_BLOCK_EXIT(freeaddrinfo, addrs);
for (addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
int family = addr->ai_family;
char host[NI_MAXHOST];
if (family == AF_INET || family == AF_INET6) {
err = getnameinfo(addr->ai_addr, addr->ai_addrlen, host,
NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (err) {
warning() << "getnameinfo() failed: " << stringifyError(err) << std::endl;
continue;
}
out.push_back(host);
}
}
if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2))) {
LogstreamBuilder builder(logger::globalLogDomain(),
getThreadName(),
logger::LogSeverity::Debug(2));
builder << "getAddrsForHost(\"" << iporhost << ":" << port << "\"):";
for (std::vector<std::string>::const_iterator o = out.begin(); o != out.end(); ++o) {
builder << " [ " << *o << "]";
}
builder << std::endl;
}
return out;
}
} // namespace
#endif // ifdef FASTPATH
bool isSelf(const HostAndPort& hostAndPort) {
#ifdef FASTPATH
// Fastpath: check if the host&port in question is bound to one
// of the interfaces on this machine.
// No need for ip match if the ports do not match
if (hostAndPort.port() == serverGlobalParams.port) {
std::vector<std::string> myAddrs = serverGlobalParams.bind_ip.empty() ?
getBoundAddrs(IPv6Enabled()) :
std::vector<std::string>();
if (!serverGlobalParams.bind_ip.empty()) {
boost::split(myAddrs, serverGlobalParams.bind_ip, boost::is_any_of(", "));
}
const std::vector<std::string> hostAddrs = getAddrsForHost(hostAndPort.host(),
hostAndPort.port(),
IPv6Enabled());
for (std::vector<std::string>::const_iterator i = myAddrs.begin();
i != myAddrs.end(); ++i) {
for (std::vector<std::string>::const_iterator j = hostAddrs.begin();
j != hostAddrs.end(); ++j) {
if (*i == *j) {
return true;
}
}
}
}
#endif // ifdef FASTPATH
if (!Listener::getTimeTracker()) {
// this ensures we are actually running a server
// this may return true later, so may want to retry
return false;
}
try {
DBClientConnection conn;
std::string errmsg;
if (!conn.connect(hostAndPort, errmsg)) {
return false;
}
if (getGlobalAuthorizationManager()->isAuthEnabled() && isInternalAuthSet()) {
if (!authenticateInternalUser(&conn)) {
return false;
}
}
BSONObj out;
bool ok = conn.simpleCommand("admin" , &out, "_isSelf");
bool me = ok && out["id"].type() == jstOID && instanceId == out["id"].OID();
return me;
}
catch (const std::exception& e) {
warning() << "could't check isSelf (" << hostAndPort << ") " << e.what() << std::endl;
}
return false;
}
/**
* Returns all the IP addresses bound to the network interfaces of this machine.
* This requires a syscall. If the ipv6enabled parameter is true, both IPv6 AND IPv4
* addresses will be returned.
*/
std::vector<std::string> getBoundAddrs(const bool ipv6enabled) {
#ifdef FASTPATH
std::vector<std::string> out;
#ifdef __linux__
ifaddrs* addrs;
int err = getifaddrs(&addrs);
if (err) {
warning() << "getifaddrs failure: " << errnoWithDescription(err) << std::endl;
return out;
}
ON_BLOCK_EXIT(freeifaddrs, addrs);
// based on example code from linux getifaddrs manpage
for (ifaddrs* addr = addrs; addr != NULL; addr = addr->ifa_next) {
if (addr->ifa_addr == NULL) continue;
int family = addr->ifa_addr->sa_family;
char host[NI_MAXHOST];
if (family == AF_INET || (ipv6enabled && (family == AF_INET6))) {
err = getnameinfo(addr->ifa_addr,
(family == AF_INET ? sizeof(struct sockaddr_in)
: sizeof(struct sockaddr_in6)),
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (err) {
warning() << "getnameinfo() failed: " << gai_strerror(err) << std::endl;
continue;
}
out.push_back(host);
}
}
#elif defined(_WIN32)
// Start with the MS recommended 15KB buffer. Use multiple attempts
// for the rare case that the adapter config changes between calls
ULONG adaptersLen = 15 * 1024;
boost::scoped_array<char> buf(new char[adaptersLen]);
IP_ADAPTER_ADDRESSES* adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(buf.get());
DWORD err;
ULONG family = ipv6enabled ? AF_UNSPEC : AF_INET;
for (int tries = 0; tries < 3; ++tries) {
err = GetAdaptersAddresses(family,
GAA_FLAG_SKIP_ANYCAST | // only want unicast addrs
GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER,
NULL,
adapters,
&adaptersLen);
if (err == ERROR_BUFFER_OVERFLOW) {
// in this case, adaptersLen will be set to the size we need to allocate
buf.reset(new char[adaptersLen]);
adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(buf.get());
}
else {
break; // only retry for incorrectly sized buffer
}
}
if (err != NO_ERROR) {
warning() << "GetAdaptersAddresses() failed: " << errnoWithDescription(err)
<< std::endl;
return out;
}
for (IP_ADAPTER_ADDRESSES* adapter = adapters;
adapter != NULL; adapter = adapter->Next) {
for (IP_ADAPTER_UNICAST_ADDRESS* addr = adapter->FirstUnicastAddress;
addr != NULL; addr = addr->Next) {
short family =
reinterpret_cast<SOCKADDR_STORAGE*>(addr->Address.lpSockaddr)->ss_family;
if (family == AF_INET) {
// IPv4
SOCKADDR_IN* sock = reinterpret_cast<SOCKADDR_IN*>(addr->Address.lpSockaddr);
char addrstr[INET_ADDRSTRLEN] = {0};
boost::system::error_code ec;
// Not all windows versions have inet_ntop
boost::asio::detail::socket_ops::inet_ntop(AF_INET,
&(sock->sin_addr),
addrstr,
INET_ADDRSTRLEN,
0,
ec);
if (ec) {
warning() << "inet_ntop failed during IPv4 address conversion: "
<< ec.message() << std::endl;
continue;
}
out.push_back(addrstr);
}
else if (family == AF_INET6) {
// IPv6
SOCKADDR_IN6* sock = reinterpret_cast<SOCKADDR_IN6*>(addr->Address.lpSockaddr);
char addrstr[INET6_ADDRSTRLEN] = {0};
boost::system::error_code ec;
boost::asio::detail::socket_ops::inet_ntop(AF_INET6,
&(sock->sin6_addr),
addrstr,
INET6_ADDRSTRLEN,
0,
ec);
if (ec) {
warning() << "inet_ntop failed during IPv6 address conversion: "
<< ec.message() << std::endl;
continue;
}
out.push_back(addrstr);
}
}
}
#endif // defined(_WIN32)
if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2))) {
LogstreamBuilder builder(logger::globalLogDomain(),
getThreadName(),
logger::LogSeverity::Debug(2));
builder << "getBoundAddrs():";
for (std::vector<std::string>::const_iterator o = out.begin(); o != out.end(); ++o) {
builder << " [ " << *o << "]";
}
builder << std::endl;
}
return out;
#else // ifdef FASTPATH
invariant(false);
#endif
}
#undef FASTPATH
} // namespace repl
} // namespace mongo

View File

@ -26,15 +26,38 @@
* it in the license file.
*/
#include "mongo/util/net/hostandport.h"
#pragma once
#include <string>
#include <vector>
#include "mongo/bson/oid.h"
namespace mongo {
struct HostAndPort;
namespace repl {
/**
* An identifier unique to this instance. Used by isSelf to see if we are talking
* to ourself or someone else.
*/
extern OID instanceId;
/**
* Returns true if "hostAndPort" identifies this instance.
*/
bool isSelf(const HostAndPort& hostAndPort);
/**
* Returns all the IP addresses bound to the network interfaces of this machine.
* This requires a syscall. If the ipv6enabled parameter is true, both IPv6 AND IPv4
* addresses will be returned.
*
* Note: this only works on Linux and Windows. All calls should be properly ifdef'd,
* otherwise an invariant will be triggered.
*/
std::vector<std::string> getBoundAddrs(const bool ipv6enabled);
} // namespace repl
} // namespace mongo

View File

@ -0,0 +1,84 @@
/**
* Copyright 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 <http://www.gnu.org/licenses/>.
*
* 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/platform/basic.h"
#include "mongo/db/repl/isself.h"
#include "mongo/db/server_options.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/net/hostandport.h"
#include "mongo/util/net/sock.h"
#include "mongo/util/scopeguard.h"
namespace mongo {
namespace repl {
namespace {
TEST(IsSelf, DetectsSameHostIPv4) {
#if defined(_WIN32) || defined(__linux__)
bool wasEnabled = IPv6Enabled();
enableIPv6(false);
ON_BLOCK_EXIT(enableIPv6, wasEnabled);
// first we get the addrs bound on this host
const std::vector<std::string> addrs = getBoundAddrs(false);
// Fastpath should agree with the result of getBoundAddrs
// since it uses it...
for (std::vector<string>::const_iterator it = addrs.begin();
it != addrs.end(); ++it) {
ASSERT(isSelf(HostAndPort(*it, serverGlobalParams.port)));
}
#else
ASSERT(true);
#endif
}
TEST(IsSelf, DetectsSameHostIPv6) {
#if defined(_WIN32) || defined(__linux__)
bool wasEnabled = IPv6Enabled();
enableIPv6(true);
ON_BLOCK_EXIT(enableIPv6, wasEnabled);
// first we get the addrs bound on this host
const std::vector<std::string> addrs = getBoundAddrs(true);
// Fastpath should agree with the result of getBoundAddrs
// since it uses it...
for (std::vector<string>::const_iterator it = addrs.begin();
it != addrs.end(); ++it) {
ASSERT(isSelf(HostAndPort(*it, serverGlobalParams.port)));
}
#else
ASSERT(true);
#endif
}
} // namespace
} // namespace repl
} // namespace mongo