PYTHON-2803 Get rid of most uses of 'master'

This change also resolves PYTHON-2848 for MongoDB 4.0.
This commit is contained in:
Bernie Hackett 2021-09-10 16:57:16 -07:00
parent 146179db53
commit ad4315134c
46 changed files with 396 additions and 384 deletions

View File

@ -431,13 +431,13 @@ can be changed to this with PyMongo 2.9 or later:
>>> from pymongo.errors import ConnectionFailure
>>> client = MongoClient(connect=False)
>>> try:
... result = client.admin.command("ismaster")
... client.admin.command("ping")
... except ConnectionFailure:
... print("Server not available")
>>>
Any operation can be used to determine if the server is available. We choose
the "ismaster" command here because it is cheap and does not require auth, so
the "ping" command here because it is cheap and does not require auth, so
it is a simple way to check whether the server is available.
The max_pool_size parameter is removed

View File

@ -532,8 +532,8 @@ class _AuthContext(object):
def speculate_command(self):
raise NotImplementedError
def parse_response(self, ismaster):
self.speculative_authenticate = ismaster.speculative_authenticate
def parse_response(self, hello):
self.speculative_authenticate = hello.speculative_authenticate
def speculate_succeeded(self):
return bool(self.speculative_authenticate)

View File

@ -51,7 +51,7 @@ MIN_SUPPORTED_SERVER_VERSION = "3.6"
MIN_SUPPORTED_WIRE_VERSION = 6
MAX_SUPPORTED_WIRE_VERSION = 13
# Frequency to call ismaster on servers, in seconds.
# Frequency to call hello on servers, in seconds.
HEARTBEAT_FREQUENCY = 10
# Frequency to clean up unclosed cursors, in seconds.
@ -67,7 +67,7 @@ EVENTS_QUEUE_FREQUENCY = 1
# longest it is willing to wait for a new primary to be found.
SERVER_SELECTION_TIMEOUT = 30
# Spec requires at least 500ms between ismaster calls.
# Spec requires at least 500ms between hello calls.
MIN_HEARTBEAT_INTERVAL = 0.5
# Spec requires at least 60s between SRV rescans.
@ -127,13 +127,13 @@ def partition_node(node):
def clean_node(node):
"""Split and normalize a node name from an ismaster response."""
"""Split and normalize a node name from a hello response."""
host, port = partition_node(node)
# Normalize hostname to lowercase, since DNS is case-insensitive:
# http://tools.ietf.org/html/rfc4343
# This prevents useless rediscovery if "foo.com" is in the seed list but
# "FOO.com" is in the ismaster response.
# "FOO.com" is in the hello response.
return host.lower(), port

View File

@ -25,6 +25,7 @@ class HelloCompat:
LEGACY_CMD = 'ismaster'
PRIMARY = 'isWritablePrimary'
LEGACY_PRIMARY = 'ismaster'
LEGACY_ERROR = 'not master'
def _get_server_type(doc):

View File

@ -29,6 +29,7 @@ from pymongo.errors import (CursorNotFound,
WriteError,
WriteConcernError,
WTimeoutError)
from pymongo.hello import HelloCompat
# From the SDAM spec, the "node is shutting down" codes.
_SHUTDOWN_CODES = frozenset([
@ -38,7 +39,7 @@ _SHUTDOWN_CODES = frozenset([
# From the SDAM spec, the "not primary" error codes are combined with the
# "node is recovering" error codes (of which the "node is shutting down"
# errors are a subset).
_NOT_MASTER_CODES = frozenset([
_NOT_PRIMARY_CODES = frozenset([
10058, # LegacyNotPrimary <=3.2 "not primary" error code
10107, # NotWritablePrimary
13435, # NotPrimaryNoSecondaryOk
@ -47,7 +48,7 @@ _NOT_MASTER_CODES = frozenset([
189, # PrimarySteppedDown
]) | _SHUTDOWN_CODES
# From the retryable writes spec.
_RETRYABLE_ERROR_CODES = _NOT_MASTER_CODES | frozenset([
_RETRYABLE_ERROR_CODES = _NOT_PRIMARY_CODES | frozenset([
7, # HostNotFound
6, # HostUnreachable
89, # NetworkTimeout
@ -150,9 +151,9 @@ def _check_command_response(response, max_wire_version,
# Server is "not primary" or "recovering"
if code is not None:
if code in _NOT_MASTER_CODES:
if code in _NOT_PRIMARY_CODES:
raise NotPrimaryError(errmsg, response)
elif "not master" in errmsg or "node is recovering" in errmsg:
elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
raise NotPrimaryError(errmsg, response)
# Other errors
@ -184,7 +185,7 @@ def _check_gle_response(result, max_wire_version):
if error_msg is None:
return result
if error_msg.startswith("not master"):
if error_msg.startswith(HelloCompat.LEGACY_ERROR):
raise NotPrimaryError(error_msg, result)
details = result

View File

@ -51,6 +51,7 @@ from pymongo.errors import (ConfigurationError,
NotPrimaryError,
OperationFailure,
ProtocolError)
from pymongo.hello import HelloCompat
from pymongo.read_concern import DEFAULT_READ_CONCERN
from pymongo.read_preferences import ReadPreference
from pymongo.write_concern import WriteConcern
@ -1524,7 +1525,7 @@ class _OpReply(object):
error_object = bson.BSON(self.documents).decode()
# Fake the ok field if it doesn't exist.
error_object.setdefault("ok", 0)
if error_object["$err"].startswith("not master"):
if error_object["$err"].startswith(HelloCompat.LEGACY_ERROR):
raise NotPrimaryError(error_object["$err"], error_object)
elif error_object.get("code") == 50:
raise ExecutionTimeout(error_object.get("$err"),

View File

@ -167,8 +167,8 @@ class MongoClient(common.BaseObject):
from pymongo.errors import ConnectionFailure
client = MongoClient()
try:
# The ismaster command is cheap and does not require auth.
client.admin.command('ismaster')
# The ping command is cheap and does not require auth.
client.admin.command('ping')
except ConnectionFailure:
print("Server not available")

View File

@ -62,7 +62,7 @@ class MonitorBase(object):
self._executor = executor
def _on_topology_gc(dummy=None):
# This prevents GC from waiting 10 seconds for isMaster to complete
# This prevents GC from waiting 10 seconds for hello to complete
# See test_cleanup_executors_on_client_del.
monitor = self_ref()
if monitor:
@ -133,7 +133,7 @@ class Monitor(MonitorBase):
self.heartbeater = None
def cancel_check(self):
"""Cancel any concurrent isMaster check.
"""Cancel any concurrent hello check.
Note: this is called from a weakref.proxy callback and MUST NOT take
any locks.
@ -204,7 +204,7 @@ class Monitor(MonitorBase):
self.close()
def _check_server(self):
"""Call isMaster or read the next streaming response.
"""Call hello or read the next streaming response.
Returns a ServerDescription.
"""
@ -213,7 +213,7 @@ class Monitor(MonitorBase):
try:
return self._check_once()
except (OperationFailure, NotPrimaryError) as exc:
# Update max cluster time even when isMaster fails.
# Update max cluster time even when hello fails.
self._topology.receive_cluster_time(
exc.details.get('$clusterTime'))
raise
@ -236,7 +236,7 @@ class Monitor(MonitorBase):
return ServerDescription(address, error=error)
def _check_once(self):
"""A single attempt to call ismaster.
"""A single attempt to call hello.
Returns a ServerDescription, or raises an exception.
"""
@ -267,19 +267,19 @@ class Monitor(MonitorBase):
cluster_time = self._topology.max_cluster_time()
start = time.monotonic()
if conn.more_to_come:
# Read the next streaming isMaster (MongoDB 4.4+).
# Read the next streaming hello (MongoDB 4.4+).
response = Hello(conn._next_reply(), awaitable=True)
elif (conn.performed_handshake and
self._server_description.topology_version):
# Initiate streaming isMaster (MongoDB 4.4+).
response = conn._ismaster(
# Initiate streaming hello (MongoDB 4.4+).
response = conn._hello(
cluster_time,
self._server_description.topology_version,
self._settings.heartbeat_frequency,
None)
else:
# New connection handshake or polling isMaster (MongoDB <4.4).
response = conn._ismaster(cluster_time, None, None, None)
# New connection handshake or polling hello (MongoDB <4.4).
response = conn._hello(cluster_time, None, None, None)
return response, time.monotonic() - start
@ -384,12 +384,12 @@ class _RttMonitor(MonitorBase):
self._pool.reset()
def _ping(self):
"""Run an "isMaster" command and return the RTT."""
"""Run a "hello" command and return the RTT."""
with self._pool.get_socket({}) as sock_info:
if self._executor._stopped:
raise Exception('_RttMonitor closed')
start = time.monotonic()
sock_info.ismaster()
sock_info.hello()
return time.monotonic() - start

View File

@ -182,6 +182,7 @@ will not add that listener to existing client instances.
from collections import abc, namedtuple
from pymongo.hello import HelloCompat
from pymongo.helpers import _handle_exception
_Listeners = namedtuple('Listeners',
@ -512,7 +513,7 @@ _SENSITIVE_COMMANDS = set(
# The "hello" command is also deemed sensitive when attempting speculative
# authentication.
def _is_speculative_authenticate(command_name, doc):
if (command_name.lower() in ('hello', 'ismaster') and
if (command_name.lower() in ('hello', HelloCompat.LEGACY_CMD) and
'speculativeAuthenticate' in doc):
return True
return False

View File

@ -246,7 +246,7 @@ def wait_for_read(sock_info, deadline):
readable = sock_info.socket_checker.select(
sock, read=True, timeout=timeout)
if context.cancelled:
raise _OperationCancelled('isMaster cancelled')
raise _OperationCancelled('hello cancelled')
if readable:
return
if deadline and time.monotonic() > deadline:

View File

@ -413,13 +413,13 @@ class PoolOptions(object):
@property
def appname(self):
"""The application name, for sending with ismaster in server handshake.
"""The application name, for sending with hello in server handshake.
"""
return self.__appname
@property
def driver(self):
"""Driver name and version, for sending with ismaster in handshake.
"""Driver name and version, for sending with hello in handshake.
"""
return self.__driver
@ -556,10 +556,10 @@ class SocketInfo(object):
else:
return SON([(HelloCompat.LEGACY_CMD, 1), ('helloOk', True)])
def ismaster(self, all_credentials=None):
return self._ismaster(None, None, None, all_credentials)
def hello(self, all_credentials=None):
return self._hello(None, None, None, all_credentials)
def _ismaster(self, cluster_time, topology_version,
def _hello(self, cluster_time, topology_version,
heartbeat_frequency, all_credentials):
cmd = self.hello_cmd()
performing_handshake = not self.performed_handshake
@ -600,36 +600,36 @@ class SocketInfo(object):
doc.setdefault('serviceId', process_id)
if not self.opts.load_balanced:
doc.pop('serviceId', None)
ismaster = Hello(doc, awaitable=awaitable)
self.is_writable = ismaster.is_writable
self.max_wire_version = ismaster.max_wire_version
self.max_bson_size = ismaster.max_bson_size
self.max_message_size = ismaster.max_message_size
self.max_write_batch_size = ismaster.max_write_batch_size
hello = Hello(doc, awaitable=awaitable)
self.is_writable = hello.is_writable
self.max_wire_version = hello.max_wire_version
self.max_bson_size = hello.max_bson_size
self.max_message_size = hello.max_message_size
self.max_write_batch_size = hello.max_write_batch_size
self.supports_sessions = (
ismaster.logical_session_timeout_minutes is not None)
self.hello_ok = ismaster.hello_ok
self.is_mongos = ismaster.server_type == SERVER_TYPE.Mongos
hello.logical_session_timeout_minutes is not None)
self.hello_ok = hello.hello_ok
self.is_mongos = hello.server_type == SERVER_TYPE.Mongos
if performing_handshake and self.compression_settings:
ctx = self.compression_settings.get_compression_context(
ismaster.compressors)
hello.compressors)
self.compression_context = ctx
self.op_msg_enabled = ismaster.max_wire_version >= 6
self.op_msg_enabled = hello.max_wire_version >= 6
if creds:
self.negotiated_mechanisms[creds] = ismaster.sasl_supported_mechs
self.negotiated_mechanisms[creds] = hello.sasl_supported_mechs
if auth_ctx:
auth_ctx.parse_response(ismaster)
auth_ctx.parse_response(hello)
if auth_ctx.speculate_succeeded():
self.auth_ctx[auth_ctx.credentials] = auth_ctx
if self.opts.load_balanced:
if not ismaster.service_id:
if not hello.service_id:
raise ConfigurationError(
'Driver attempted to initialize in load balancing mode,'
' but the server does not support this mode')
self.service_id = ismaster.service_id
self.service_id = hello.service_id
self.generation = self.pool_gen.get(self.service_id)
return ismaster
return hello
def _next_reply(self):
reply = self.receive_message(None)
@ -1110,7 +1110,7 @@ class Pool:
:Parameters:
- `address`: a (hostname, port) tuple
- `options`: a PoolOptions instance
- `handshake`: whether to call ismaster for each new SocketInfo
- `handshake`: whether to call hello for each new SocketInfo
"""
if options.pause_enabled:
self.state = PoolState.PAUSED
@ -1338,7 +1338,7 @@ class Pool:
sock_info = SocketInfo(sock, self, self.address, conn_id)
try:
if self.handshake:
sock_info.ismaster(all_credentials)
sock_info.hello(all_credentials)
self.is_writable = sock_info.is_writable
sock_info.check_auth(all_credentials)

View File

@ -519,7 +519,7 @@ class MovingAverage(object):
def add_sample(self, sample):
if sample < 0:
# Likely system time change while waiting for ismaster response
# Likely system time change while waiting for hello response
# and not using time.monotonic. Ignore it, the next one will
# probably be valid.
return

View File

@ -26,7 +26,7 @@ class ServerDescription(object):
:Parameters:
- `address`: A (host, port) pair
- `ismaster`: Optional Hello instance
- `hello`: Optional Hello instance
- `round_trip_time`: Optional float
- `error`: Optional, the last error attempting to connect to the server
"""
@ -43,41 +43,41 @@ class ServerDescription(object):
def __init__(
self,
address,
ismaster=None,
hello=None,
round_trip_time=None,
error=None):
self._address = address
if not ismaster:
ismaster = Hello({})
if not hello:
hello = Hello({})
self._server_type = ismaster.server_type
self._all_hosts = ismaster.all_hosts
self._tags = ismaster.tags
self._replica_set_name = ismaster.replica_set_name
self._primary = ismaster.primary
self._max_bson_size = ismaster.max_bson_size
self._max_message_size = ismaster.max_message_size
self._max_write_batch_size = ismaster.max_write_batch_size
self._min_wire_version = ismaster.min_wire_version
self._max_wire_version = ismaster.max_wire_version
self._set_version = ismaster.set_version
self._election_id = ismaster.election_id
self._cluster_time = ismaster.cluster_time
self._is_writable = ismaster.is_writable
self._is_readable = ismaster.is_readable
self._ls_timeout_minutes = ismaster.logical_session_timeout_minutes
self._server_type = hello.server_type
self._all_hosts = hello.all_hosts
self._tags = hello.tags
self._replica_set_name = hello.replica_set_name
self._primary = hello.primary
self._max_bson_size = hello.max_bson_size
self._max_message_size = hello.max_message_size
self._max_write_batch_size = hello.max_write_batch_size
self._min_wire_version = hello.min_wire_version
self._max_wire_version = hello.max_wire_version
self._set_version = hello.set_version
self._election_id = hello.election_id
self._cluster_time = hello.cluster_time
self._is_writable = hello.is_writable
self._is_readable = hello.is_readable
self._ls_timeout_minutes = hello.logical_session_timeout_minutes
self._round_trip_time = round_trip_time
self._me = ismaster.me
self._me = hello.me
self._last_update_time = time.monotonic()
self._error = error
self._topology_version = ismaster.topology_version
self._topology_version = hello.topology_version
if error:
if hasattr(error, 'details') and isinstance(error.details, dict):
self._topology_version = error.details.get('topologyVersion')
if ismaster.last_write_date:
if hello.last_write_date:
# Convert from datetime to seconds.
delta = ismaster.last_write_date - EPOCH_NAIVE
delta = hello.last_write_date - EPOCH_NAIVE
self._last_write_date = delta.total_seconds()
else:
self._last_write_date = None

View File

@ -278,7 +278,7 @@ class Topology(object):
td_old = self._description
sd_old = td_old._server_descriptions[server_description.address]
if _is_stale_server_description(sd_old, server_description):
# This is a stale isMaster response. Ignore it.
# This is a stale hello response. Ignore it.
return
new_td = updated_topology_description(
@ -326,10 +326,10 @@ class Topology(object):
self._condition.notify_all()
def on_change(self, server_description, reset_pool=False):
"""Process a new ServerDescription after an ismaster call completes."""
"""Process a new ServerDescription after an hello call completes."""
# We do no I/O holding the lock.
with self._lock:
# Monitors may continue working on ismaster calls for some time
# Monitors may continue working on hello calls for some time
# after a call to Topology.close, so this method may be called at
# any time. Ensure the topology is open before processing the
# change.
@ -626,7 +626,7 @@ class Topology(object):
err_code = error.code
else:
err_code = error.details.get('code', -1)
if err_code in helpers._NOT_MASTER_CODES:
if err_code in helpers._NOT_PRIMARY_CODES:
is_shutting_down = err_code in helpers._SHUTDOWN_CODES
# Mark server Unknown, clear the pool, and request check.
if not self._settings.load_balanced:
@ -649,7 +649,7 @@ class Topology(object):
# Clear the pool.
server.reset(service_id)
# "When a client marks a server Unknown from `Network error when
# reading or writing`_, clients MUST cancel the isMaster check on
# reading or writing`_, clients MUST cancel the hello check on
# that server and close the current monitoring connection."
server._monitor.cancel_check()

View File

@ -67,7 +67,7 @@ class TopologyDescription(object):
self._init_incompatible_err()
# Server Discovery And Monitoring Spec: Whenever a client updates the
# TopologyDescription from an ismaster response, it MUST set
# TopologyDescription from an hello response, it MUST set
# TopologyDescription.logicalSessionTimeoutMinutes to the smallest
# logicalSessionTimeoutMinutes value among ServerDescriptions of all
# data-bearing server types. If any have a null
@ -303,7 +303,7 @@ class TopologyDescription(object):
self.topology_type_name, servers)
# If topology type is Unknown and we receive an ismaster response, what should
# If topology type is Unknown and we receive a hello response, what should
# the new topology type be?
_SERVER_TYPE_TO_TOPOLOGY_TYPE = {
SERVER_TYPE.Mongos: TOPOLOGY_TYPE.Sharded,
@ -321,9 +321,9 @@ def updated_topology_description(topology_description, server_description):
:Parameters:
- `topology_description`: the current TopologyDescription
- `server_description`: a new ServerDescription that resulted from
an ismaster call
a hello call
Called after attempting (successfully or not) to call ismaster on the
Called after attempting (successfully or not) to call hello on the
server at server_description.address. Does not modify topology_description.
"""
address = server_description.address
@ -436,7 +436,7 @@ def _updated_topology_description_srv_polling(topology_description, seedlist):
:Parameters:
- `topology_description`: the current TopologyDescription
- `seedlist`: a list of new seeds new ServerDescription that resulted from
an ismaster call
a hello call
"""
# Create a copy of the server descriptions.
sds = topology_description.server_descriptions()
@ -470,7 +470,7 @@ def _update_rs_from_primary(
server_description,
max_set_version,
max_election_id):
"""Update topology description from a primary's ismaster response.
"""Update topology description from a primary's hello response.
Pass in a dict of ServerDescriptions, current replica set name, the
ServerDescription we are processing, and the TopologyDescription's

View File

@ -111,7 +111,7 @@ def parse_host(entity, default_port=DEFAULT_PORT):
# Normalize hostname to lowercase, since DNS is case-insensitive:
# http://tools.ietf.org/html/rfc4343
# This prevents useless rediscovery if "foo.com" is in the seed list but
# "FOO.com" is in the ismaster response.
# "FOO.com" is in the hello response.
return host.lower(), port

View File

@ -48,6 +48,7 @@ import pymongo.errors
from bson.son import SON
from pymongo import common, message
from pymongo.common import partition_node
from pymongo.hello import HelloCompat
from pymongo.server_api import ServerApi
from pymongo.ssl_support import HAVE_SSL, _ssl
from pymongo.uri_parser import parse_uri
@ -270,8 +271,8 @@ class ClientContext(object):
return opts
@property
def ismaster(self):
return self.client.admin.command('isMaster')
def hello(self):
return self.client.admin.command(HelloCompat.LEGACY_CMD)
def _connect(self, host, port, **kwargs):
# Jython takes a long time to connect.
@ -284,11 +285,11 @@ class ClientContext(object):
host, port, serverSelectionTimeoutMS=timeout_ms, **kwargs)
try:
try:
client.admin.command('isMaster') # Can we connect?
client.admin.command(HelloCompat.LEGACY_CMD) # Can we connect?
except pymongo.errors.OperationFailure as exc:
# SERVER-32063
self.connection_attempts.append(
'connected client %r, but isMaster failed: %s' % (
'connected client %r, but legacy hello failed: %s' % (
client, exc))
else:
self.connection_attempts.append(
@ -365,11 +366,11 @@ class ClientContext(object):
# MMAPv1 does not support retryWrites=True.
self.default_client_options['retryWrites'] = False
ismaster = self.ismaster
self.sessions_enabled = 'logicalSessionTimeoutMinutes' in ismaster
hello = self.hello
self.sessions_enabled = 'logicalSessionTimeoutMinutes' in hello
if 'setName' in ismaster:
self.replica_set_name = str(ismaster['setName'])
if 'setName' in hello:
self.replica_set_name = str(hello['setName'])
self.is_rs = True
if self.auth_enabled:
# It doesn't matter which member we use as the seed here.
@ -387,18 +388,18 @@ class ClientContext(object):
replicaSet=self.replica_set_name,
**self.default_client_options)
# Get the authoritative ismaster result from the primary.
ismaster = self.ismaster
# Get the authoritative hello result from the primary.
hello = self.hello
nodes = [partition_node(node.lower())
for node in ismaster.get('hosts', [])]
for node in hello.get('hosts', [])]
nodes.extend([partition_node(node.lower())
for node in ismaster.get('passives', [])])
for node in hello.get('passives', [])])
nodes.extend([partition_node(node.lower())
for node in ismaster.get('arbiters', [])])
for node in hello.get('arbiters', [])])
self.nodes = set(nodes)
else:
self.nodes = set([(host, port)])
self.w = len(ismaster.get("hosts", [])) or 1
self.w = len(hello.get("hosts", [])) or 1
self.version = Version.from_client(self.client)
if TEST_SERVERLESS:
@ -419,7 +420,7 @@ class ClientContext(object):
self.test_commands_enabled = True
self.has_ipv6 = self._server_started_with_ipv6()
self.is_mongos = (self.ismaster.get('msg') == 'isdbgrid')
self.is_mongos = (self.hello.get('msg') == 'isdbgrid')
if self.is_mongos:
if self.serverless:
self.mongoses.append(self.client.address)
@ -432,8 +433,8 @@ class ClientContext(object):
mongos_client = self._connect(
*next_address, **self.default_client_options)
if mongos_client:
ismaster = mongos_client.admin.command('ismaster')
if ismaster.get('msg') == 'isdbgrid':
hello = mongos_client.admin.command(HelloCompat.LEGACY_CMD)
if hello.get('msg') == 'isdbgrid':
self.mongoses.append(next_address)
def init(self):

View File

@ -57,7 +57,7 @@ def connect(uri):
raise Exception("Must set env variable to test.")
client = pymongo.MongoClient(uri)
# No TLS error
client.admin.command('ismaster')
client.admin.command('ping')
# No auth error
client.test.test.count_documents({})

View File

@ -25,12 +25,13 @@ repository_path = os.path.normpath(os.path.join(this_path, '..', '..'))
sys.path.insert(0, repository_path)
import pymongo
from pymongo.hello import HelloCompat
from pymongo.mongo_client import MongoClient
client = MongoClient()
# If the deployment is a replica set, connect to the whole set.
replica_set_name = client.admin.command('ismaster').get('setName')
replica_set_name = client.admin.command(HelloCompat.LEGACY_CMD).get('setName')
if replica_set_name:
client = MongoClient(replicaSet=replica_set_name)

View File

@ -46,7 +46,7 @@ def _connect(options):
"&tlsCAFile=%s&%s") % (TIMEOUT_MS, CA_FILE, options)
print(uri)
client = pymongo.MongoClient(uri)
client.admin.command('ismaster')
client.admin.command('ping')
class TestOCSP(unittest.TestCase):

View File

@ -189,7 +189,7 @@ class TestRunCommand(PerformanceTest, unittest.TestCase):
def do_task(self):
command = self.client.perftest.command
for _ in range(NUM_DOCS):
command("ismaster")
command("ping")
class TestDocument(PerformanceTest):

View File

@ -21,7 +21,7 @@ import weakref
from pymongo import common
from pymongo import MongoClient
from pymongo.errors import AutoReconnect, NetworkTimeout
from pymongo.hello import Hello
from pymongo.hello import Hello, HelloCompat
from pymongo.monitor import Monitor
from pymongo.pool import Pool
from pymongo.server_description import ServerDescription
@ -99,20 +99,20 @@ class MockMonitor(Monitor):
def _check_once(self):
client = self.client
address = self._server_description.address
response, rtt = client.mock_is_master('%s:%d' % address)
response, rtt = client.mock_hello('%s:%d' % address)
return ServerDescription(address, Hello(response), rtt)
class MockClient(MongoClient):
def __init__(
self, standalones, members, mongoses, ismaster_hosts=None,
self, standalones, members, mongoses, hello_hosts=None,
arbiters=None, down_hosts=None, *args, **kwargs):
"""A MongoClient connected to the default server, with a mock topology.
standalones, members, mongoses, arbiters, and down_hosts determine the
configuration of the topology. They are formatted like ['a:1', 'b:2'].
ismaster_hosts provides an alternative host list for the server's
mocked ismaster response; see test_connect_with_internal_ips.
hello_hosts provides an alternative host list for the server's
mocked hello response; see test_connect_with_internal_ips.
"""
self.mock_standalones = standalones[:]
self.mock_members = members[:]
@ -125,10 +125,10 @@ class MockClient(MongoClient):
# Hosts that should be considered an arbiter.
self.mock_arbiters = arbiters[:] if arbiters else []
if ismaster_hosts is not None:
self.mock_ismaster_hosts = ismaster_hosts
if hello_hosts is not None:
self.mock_hello_hosts = hello_hosts
else:
self.mock_ismaster_hosts = members[:]
self.mock_hello_hosts = members[:]
self.mock_mongoses = mongoses[:]
@ -166,8 +166,8 @@ class MockClient(MongoClient):
def set_max_write_batch_size(self, host, size):
self.mock_max_write_batch_sizes[host] = size
def mock_is_master(self, host):
"""Return mock ismaster response (a dict) and round trip time."""
def mock_hello(self, host):
"""Return mock hello response (a dict) and round trip time."""
if host in self.mock_wire_versions:
min_wire_version, max_wire_version = self.mock_wire_versions[host]
else:
@ -186,20 +186,20 @@ class MockClient(MongoClient):
elif host in self.mock_standalones:
response = {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'minWireVersion': min_wire_version,
'maxWireVersion': max_wire_version,
'maxWriteBatchSize': max_write_batch_size}
elif host in self.mock_members:
ismaster = (host == self.mock_primary)
primary = (host == self.mock_primary)
# Simulate a replica set member.
response = {
'ok': 1,
'ismaster': ismaster,
'secondary': not ismaster,
HelloCompat.LEGACY_CMD: primary,
'secondary': not primary,
'setName': 'rs',
'hosts': self.mock_ismaster_hosts,
'hosts': self.mock_hello_hosts,
'minWireVersion': min_wire_version,
'maxWireVersion': max_wire_version,
'maxWriteBatchSize': max_write_batch_size}
@ -213,14 +213,14 @@ class MockClient(MongoClient):
elif host in self.mock_mongoses:
response = {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'minWireVersion': min_wire_version,
'maxWireVersion': max_wire_version,
'msg': 'isdbgrid',
'maxWriteBatchSize': max_write_batch_size}
else:
# In test_internal_ips(), we try to connect to a host listed
# in ismaster['hosts'] but not publicly accessible.
# in hello['hosts'] but not publicly accessible.
raise AutoReconnect('Unknown host: %s' % host)
return response, rtt

View File

@ -25,6 +25,7 @@ sys.path[0:0] = [""]
from pymongo import MongoClient, monitoring
from pymongo.auth import HAVE_KERBEROS, _build_credentials_tuple
from pymongo.errors import OperationFailure
from pymongo.hello import HelloCompat
from pymongo.read_preferences import ReadPreference
from pymongo.saslprep import HAVE_STRINGPREP
from test import client_context, IntegrationTest, SkipTest, unittest, Version
@ -155,7 +156,7 @@ class TestGSSAPI(unittest.TestCase):
client = MongoClient(mech_uri)
client[GSSAPI_DB].collection.find_one()
set_name = client.admin.command('ismaster').get('setName')
set_name = client.admin.command('HelloCompat.LEGACY_CMD').get('setName')
if set_name:
if not self.service_realm_required:
# Without authMechanismProperties
@ -221,7 +222,7 @@ class TestGSSAPI(unittest.TestCase):
thread.join()
self.assertTrue(thread.success)
set_name = client.admin.command('ismaster').get('setName')
set_name = client.admin.command('HelloCompat.LEGACY_CMD').get('setName')
if set_name:
client = MongoClient(GSSAPI_HOST,
GSSAPI_PORT,
@ -269,7 +270,7 @@ class TestSASLPlain(unittest.TestCase):
client = MongoClient(uri)
client.ldap.test.find_one()
set_name = client.admin.command('ismaster').get('setName')
set_name = client.admin.command('HelloCompat.LEGACY_CMD').get('setName')
if set_name:
client = MongoClient(SASL_HOST,
SASL_PORT,
@ -299,8 +300,8 @@ class TestSASLPlain(unittest.TestCase):
bad_user = MongoClient(auth_string('not-user', SASL_PASS))
bad_pwd = MongoClient(auth_string(SASL_USER, 'not-pwd'))
# OperationFailure raised upon connecting.
self.assertRaises(OperationFailure, bad_user.admin.command, 'ismaster')
self.assertRaises(OperationFailure, bad_pwd.admin.command, 'ismaster')
self.assertRaises(OperationFailure, bad_user.admin.command, 'ping')
self.assertRaises(OperationFailure, bad_pwd.admin.command, 'ping')
class TestSCRAMSHA1(IntegrationTest):
@ -530,7 +531,7 @@ class TestSCRAM(IntegrationTest):
def test_cache(self):
client = single_client()
# Force authentication.
client.admin.command('ismaster')
client.admin.command('ping')
all_credentials = client._MongoClient__all_credentials
credentials = all_credentials.get('admin')
cache = credentials.cache

View File

@ -776,8 +776,8 @@ class TestBulkWriteConcern(BulkTestBase):
cls.w = client_context.w
cls.secondary = None
if cls.w > 1:
for member in client_context.ismaster['hosts']:
if member != client_context.ismaster['primary']:
for member in client_context.hello['hosts']:
if member != client_context.hello['primary']:
cls.secondary = single_client(*partition_node(member))
break

View File

@ -36,11 +36,12 @@ from bson.son import SON
from bson.tz_util import utc
import pymongo
from pymongo import message, monitoring
from pymongo.common import CONNECT_TIMEOUT, _UUID_REPRESENTATIONS
from pymongo.command_cursor import CommandCursor
from pymongo.common import CONNECT_TIMEOUT, _UUID_REPRESENTATIONS
from pymongo.compression_support import _HAVE_SNAPPY, _HAVE_ZSTD
from pymongo.cursor import Cursor, CursorType
from pymongo.database import Database
from pymongo.driver_info import DriverInfo
from pymongo.errors import (AutoReconnect,
ConfigurationError,
ConnectionFailure,
@ -50,10 +51,10 @@ from pymongo.errors import (AutoReconnect,
OperationFailure,
ServerSelectionTimeoutError,
WriteConcernError)
from pymongo.hello import HelloCompat
from pymongo.mongo_client import MongoClient
from pymongo.monitoring import (ServerHeartbeatListener,
ServerHeartbeatStartedEvent)
from pymongo.mongo_client import MongoClient
from pymongo.driver_info import DriverInfo
from pymongo.pool import SocketInfo, _METADATA
from pymongo.read_preferences import ReadPreference
from pymongo.server_description import ServerDescription
@ -61,9 +62,9 @@ from pymongo.server_selectors import (readable_server_selector,
writable_server_selector)
from pymongo.server_type import SERVER_TYPE
from pymongo.settings import TOPOLOGY_TYPE
from pymongo.srv_resolver import _HAVE_DNSPYTHON
from pymongo.topology import _ErrorContext
from pymongo.topology_description import TopologyDescription
from pymongo.srv_resolver import _HAVE_DNSPYTHON
from pymongo.write_concern import WriteConcern
from test import (client_context,
client_knobs,
@ -833,7 +834,7 @@ class TestClient(IntegrationTest):
self.assertTrue(client._kill_cursors_executor._stopped)
# Reusing the closed client should restart the thread.
client.admin.command('isMaster')
client.admin.command('ping')
self.assertFalse(client._kill_cursors_executor._stopped)
# Again, closing the client should stop the thread.
@ -850,7 +851,7 @@ class TestClient(IntegrationTest):
self.assertFalse(kc_thread and kc_thread.is_alive())
# Using the client should open topology and start the thread.
client.admin.command('isMaster')
client.admin.command('ping')
self.assertTrue(client._topology._opened)
kc_thread = client._kill_cursors_executor._thread
self.assertTrue(kc_thread and kc_thread.is_alive())
@ -1783,11 +1784,11 @@ class TestClientLazyConnect(IntegrationTest):
c = self._get_client()
# max_bson_size will cause the client to connect.
ismaster = c.db.command('ismaster')
self.assertEqual(ismaster['maxBsonObjectSize'], c.max_bson_size)
if 'maxMessageSizeBytes' in ismaster:
hello = c.db.command(HelloCompat.LEGACY_CMD)
self.assertEqual(hello['maxBsonObjectSize'], c.max_bson_size)
if 'maxMessageSizeBytes' in hello:
self.assertEqual(
ismaster['maxMessageSizeBytes'],
hello['maxMessageSizeBytes'],
c.max_message_size)

View File

@ -318,11 +318,11 @@ class TestCMAP(IntegrationTest):
def test_2_all_client_pools_have_same_options(self):
client = rs_or_single_client(**self.POOL_OPTIONS)
self.addCleanup(client.close)
client.admin.command('isMaster')
client.admin.command('ping')
# Discover at least one secondary.
if client_context.has_secondaries:
client.admin.command(
'isMaster', read_preference=ReadPreference.SECONDARY)
'ping', read_preference=ReadPreference.SECONDARY)
pools = get_pools(client)
pool_opts = pools[0].opts
@ -346,7 +346,7 @@ class TestCMAP(IntegrationTest):
self.assertEqual(listener.event_count(PoolCreatedEvent), 1)
# Creates a new connection.
client.admin.command('isMaster')
client.admin.command('ping')
self.assertEqual(
listener.event_count(ConnectionCheckOutStartedEvent), 1)
self.assertEqual(listener.event_count(ConnectionCreatedEvent), 1)
@ -355,7 +355,7 @@ class TestCMAP(IntegrationTest):
self.assertEqual(listener.event_count(ConnectionCheckedInEvent), 1)
# Uses the existing connection.
client.admin.command('isMaster')
client.admin.command('ping')
self.assertEqual(
listener.event_count(ConnectionCheckOutStartedEvent), 2)
self.assertEqual(listener.event_count(ConnectionCheckedOutEvent), 2)
@ -379,7 +379,7 @@ class TestCMAP(IntegrationTest):
# Attempt to create a new connection.
with self.assertRaisesRegex(ConnectionFailure, 'connect failed'):
client.admin.command('isMaster')
client.admin.command('ping')
self.assertIsInstance(listener.events[0], PoolCreatedEvent)
self.assertIsInstance(listener.events[1], PoolReadyEvent)
@ -401,7 +401,7 @@ class TestCMAP(IntegrationTest):
# Attempt to create a new connection.
with self.assertRaisesRegex(OperationFailure, 'failed'):
client.admin.command('isMaster')
client.admin.command('ping')
self.assertIsInstance(listener.events[0], PoolCreatedEvent)
self.assertIsInstance(listener.events[1], PoolReadyEvent)

View File

@ -2134,7 +2134,7 @@ class TestCollection(IntegrationTest):
c_default = db.get_collection('test', write_concern=WriteConcern())
results = listener.results
# Authenticate the client and throw out auth commands from the listener.
db.command('ismaster')
db.command('ping')
results.clear()
if client_context.version.at_least(3, 1, 9, -1):
c_w0.find_one_and_update(

View File

@ -122,13 +122,13 @@ class TestConnectionsSurvivePrimaryStepDown(IntegrationTest):
@client_context.require_version_min(4, 2, -1)
@client_context.require_test_commands
def test_not_master_keep_connection_pool(self):
def test_not_primary_keep_connection_pool(self):
self.run_scenario(10107, True, self.verify_pool_not_cleared)
@client_context.require_version_min(4, 0, 0)
@client_context.require_version_max(4, 1, 0, -1)
@client_context.require_test_commands
def test_not_master_reset_connection_pool(self):
def test_not_primary_reset_connection_pool(self):
self.run_scenario(10107, False, self.verify_pool_cleared)
@client_context.require_version_min(4, 0, 0)

View File

@ -31,7 +31,7 @@ from pymongo.errors import (AutoReconnect,
OperationFailure)
from pymongo.helpers import (_check_command_response,
_check_write_command_response)
from pymongo.hello import Hello
from pymongo.hello import Hello, HelloCompat
from pymongo.server_description import ServerDescription, SERVER_TYPE
from pymongo.settings import TopologySettings
from pymongo.topology import Topology, _ErrorContext
@ -81,9 +81,9 @@ def create_mock_topology(uri, monitor_class=DummyMonitor):
return c
def got_ismaster(topology, server_address, ismaster_response):
def got_hello(topology, server_address, hello_response):
server_description = ServerDescription(
server_address, Hello(ismaster_response), 0)
server_address, Hello(hello_response), 0)
topology.on_change(server_description)
@ -206,7 +206,7 @@ def create_test(scenario_def):
description = phase.get('description', str(i))
with assertion_context('phase: %s' % (description,)):
for response in phase.get('responses', []):
got_ismaster(
got_hello(
c, common.partition_node(response[0]), response[1])
for app_error in phase.get('applicationErrors', []):
@ -244,12 +244,12 @@ class TestClusterTimeComparison(unittest.TestCase):
def send_cluster_time(time, inc, should_update):
old = t.max_cluster_time()
new = {'clusterTime': Timestamp(time, inc)}
got_ismaster(t,
('host', 27017),
{'ok': 1,
'minWireVersion': 0,
'maxWireVersion': 6,
'$clusterTime': new})
got_hello(t,
('host', 27017),
{'ok': 1,
'minWireVersion': 0,
'maxWireVersion': 6,
'$clusterTime': new})
actual = t.max_cluster_time()
if should_update:
@ -332,15 +332,15 @@ class TestPoolManagement(IntegrationTest):
listener.events.index(hb_succeeded))
listener.reset()
fail_ismaster = {
fail_hello = {
'mode': {'times': 2},
'data': {
'failCommands': ['isMaster', 'hello'],
'failCommands': [HelloCompat.LEGACY_CMD, 'hello'],
'errorCode': 1234,
'appName': 'SDAMPoolManagementTest',
},
}
with self.fail_point(fail_ismaster):
with self.fail_point(fail_hello):
listener.wait_for_event(monitoring.ServerHeartbeatFailedEvent, 1)
listener.wait_for_event(monitoring.PoolClearedEvent, 1)
listener.wait_for_event(

View File

@ -264,11 +264,11 @@ class TestClientSimple(EncryptionIntegrationTest):
client = rs_or_single_client(auto_encryption_opts=opts)
self.addCleanup(client.close)
client.admin.command('isMaster')
client.admin.command('ping')
client.close()
with self.assertRaisesRegex(InvalidOperation,
'Cannot use MongoClient after close'):
client.admin.command('isMaster')
client.admin.command('ping')
class TestClientMaxWireVersion(IntegrationTest):
@ -287,7 +287,7 @@ class TestClientMaxWireVersion(IntegrationTest):
with self.assertRaisesRegex(ConfigurationError, msg):
client.test.test.insert_one({})
with self.assertRaisesRegex(ConfigurationError, msg):
client.admin.command('isMaster')
client.admin.command('ping')
with self.assertRaisesRegex(ConfigurationError, msg):
client.test.test.find_one({})
with self.assertRaisesRegex(ConfigurationError, msg):

View File

@ -62,7 +62,7 @@ class TestErrors(PyMongoTestCase):
{"errmsg": 'unicode \U0001f40d'})
self._test_unicode_strs(exc)
def test_unicode_strs_not_master_error(self):
def test_unicode_strs_not_primary_error(self):
exc = NotPrimaryError('unicode \U0001f40d',
{"errmsg": 'unicode \U0001f40d'})
self._test_unicode_strs(exc)

View File

@ -20,7 +20,7 @@ import threading
sys.path[0:0] = [""]
from pymongo.errors import ConnectionFailure
from pymongo.hello import Hello
from pymongo.hello import Hello, HelloCompat
from pymongo.monitor import Monitor
from test import unittest, client_knobs, IntegrationTest
from test.utils import (HeartbeatEventListener, MockPool, single_client,
@ -74,7 +74,7 @@ class TestHeartbeatMonitoring(IntegrationTest):
def test_standalone(self):
responses = (('a', 27017),
{
"ismaster": True,
HelloCompat.LEGACY_CMD: True,
"maxWireVersion": 4,
"minWireVersion": 0,
"ok": 1

View File

@ -41,7 +41,7 @@ class SimpleOp(threading.Thread):
self.passed = False
def run(self):
self.client.db.command('ismaster')
self.client.db.command('ping')
self.passed = True # No exception raised.
@ -121,10 +121,10 @@ class TestMongosLoadBalancing(MockClientTest):
def f():
try:
client.db.command('ismaster')
client.db.command('ping')
except AutoReconnect:
# Second attempt succeeds.
client.db.command('ismaster')
client.db.command('ping')
passed.append(True)
@ -151,23 +151,23 @@ class TestMongosLoadBalancing(MockClientTest):
writable_addresses(topology))
# No error
client.admin.command('ismaster')
client.admin.command('ping')
client = connected(self.mock_client(localThresholdMS=0))
self.assertEqual(0, client.local_threshold_ms)
# No error
client.db.command('ismaster')
client.db.command('ping')
# Our chosen mongos goes down.
client.kill_host('%s:%s' % next(iter(client.nodes)))
try:
client.db.command('ismaster')
client.db.command('ping')
except:
pass
# We eventually connect to a new mongos.
def connect_to_new_mongos():
try:
return client.db.command('ismaster')
return client.db.command('ping')
except AutoReconnect:
pass
wait_until(connect_to_new_mongos, 'connect to a new mongos')

View File

@ -63,7 +63,7 @@ class TestCommandMonitoring(IntegrationTest):
super(TestCommandMonitoring, self).tearDown()
def test_started_simple(self):
self.client.pymongo_test.command('ismaster')
self.client.pymongo_test.command('ping')
results = self.listener.results
started = results['started'][0]
succeeded = results['succeeded'][0]
@ -72,14 +72,14 @@ class TestCommandMonitoring(IntegrationTest):
isinstance(succeeded, monitoring.CommandSucceededEvent))
self.assertTrue(
isinstance(started, monitoring.CommandStartedEvent))
self.assertEqualCommand(SON([('ismaster', 1)]), started.command)
self.assertEqual('ismaster', started.command_name)
self.assertEqualCommand(SON([('ping', 1)]), started.command)
self.assertEqual('ping', started.command_name)
self.assertEqual(self.client.address, started.connection_id)
self.assertEqual('pymongo_test', started.database_name)
self.assertTrue(isinstance(started.request_id, int))
def test_succeeded_simple(self):
self.client.pymongo_test.command('ismaster')
self.client.pymongo_test.command('ping')
results = self.listener.results
started = results['started'][0]
succeeded = results['succeeded'][0]
@ -88,7 +88,7 @@ class TestCommandMonitoring(IntegrationTest):
isinstance(started, monitoring.CommandStartedEvent))
self.assertTrue(
isinstance(succeeded, monitoring.CommandSucceededEvent))
self.assertEqual('ismaster', succeeded.command_name)
self.assertEqual('ping', succeeded.command_name)
self.assertEqual(self.client.address, succeeded.connection_id)
self.assertEqual(1, succeeded.reply.get('ok'))
self.assertTrue(isinstance(succeeded.request_id, int))
@ -432,11 +432,11 @@ class TestCommandMonitoring(IntegrationTest):
@client_context.require_replica_set
@client_context.require_secondaries_count(1)
def test_not_master_error(self):
def test_not_primary_error(self):
address = next(iter(client_context.client.secondaries))
client = single_client(*address, event_listeners=[self.listener])
# Clear authentication command results from the listener.
client.admin.command('ismaster')
client.admin.command('ping')
self.listener.results.clear()
error = None
try:
@ -982,7 +982,7 @@ class TestCommandMonitoring(IntegrationTest):
'data': {
'failCommands': ['insert'],
'closeConnection': False,
'errorCode': 10107, # NotMaster
'errorCode': 10107, # Not primary
},
}
with self.fail_point(insert_command_error):
@ -1126,7 +1126,7 @@ class TestGlobalListener(IntegrationTest):
monitoring.register(cls.listener)
cls.client = single_client()
# Get one (authenticated) socket in the pool.
cls.client.pymongo_test.command('ismaster')
cls.client.pymongo_test.command('ping')
@classmethod
def tearDownClass(cls):
@ -1139,7 +1139,7 @@ class TestGlobalListener(IntegrationTest):
self.listener.results.clear()
def test_simple(self):
self.client.pymongo_test.command('ismaster')
self.client.pymongo_test.command('ping')
results = self.listener.results
started = results['started'][0]
succeeded = results['succeeded'][0]
@ -1148,8 +1148,8 @@ class TestGlobalListener(IntegrationTest):
isinstance(succeeded, monitoring.CommandSucceededEvent))
self.assertTrue(
isinstance(started, monitoring.CommandStartedEvent))
self.assertEqualCommand(SON([('ismaster', 1)]), started.command)
self.assertEqual('ismaster', started.command_name)
self.assertEqualCommand(SON([('ping', 1)]), started.command)
self.assertEqual('ping', started.command_name)
self.assertEqual(self.client.address, started.connection_id)
self.assertEqual('pymongo_test', started.database_name)
self.assertTrue(isinstance(started.request_id, int))
@ -1160,27 +1160,27 @@ class TestEventClasses(unittest.TestCase):
def test_command_event_repr(self):
request_id, connection_id, operation_id = 1, ('localhost', 27017), 2
event = monitoring.CommandStartedEvent(
{'isMaster': 1}, 'admin', request_id, connection_id, operation_id)
{'ping': 1}, 'admin', request_id, connection_id, operation_id)
self.assertEqual(
repr(event),
"<CommandStartedEvent ('localhost', 27017) db: 'admin', "
"command: 'isMaster', operation_id: 2, service_id: None>")
"command: 'ping', operation_id: 2, service_id: None>")
delta = datetime.timedelta(milliseconds=100)
event = monitoring.CommandSucceededEvent(
delta, {'ok': 1}, 'isMaster', request_id, connection_id,
delta, {'ok': 1}, 'ping', request_id, connection_id,
operation_id)
self.assertEqual(
repr(event),
"<CommandSucceededEvent ('localhost', 27017) "
"command: 'isMaster', operation_id: 2, duration_micros: 100000, "
"command: 'ping', operation_id: 2, duration_micros: 100000, "
"service_id: None>")
event = monitoring.CommandFailedEvent(
delta, {'ok': 0}, 'isMaster', request_id, connection_id,
delta, {'ok': 0}, 'ping', request_id, connection_id,
operation_id)
self.assertEqual(
repr(event),
"<CommandFailedEvent ('localhost', 27017) "
"command: 'isMaster', operation_id: 2, duration_micros: 100000, "
"command: 'ping', operation_id: 2, duration_micros: 100000, "
"failure: {'ok': 0}, service_id: None>")
def test_server_heartbeat_event_repr(self):

View File

@ -281,7 +281,7 @@ class TestPooling(_TestPoolingBase):
self.assertTrue(socket_checker.select(s, write=True, timeout=.05))
# Make the socket readable
_, msg, _ = message._query(
0, 'admin.$cmd', 0, -1, SON([('isMaster', 1)]), None,
0, 'admin.$cmd', 0, -1, SON([('ping', 1)]), None,
DEFAULT_CODEC_OPTIONS)
s.sendall(msg)
# Block until the socket is readable.

View File

@ -61,7 +61,7 @@ class TestSecondaryBecomesStandalone(MockClientTest):
c.close()
with self.assertRaises(AutoReconnect):
c.db.command('ismaster')
c.db.command('ping')
self.assertEqual(c.address, None)
@ -106,7 +106,7 @@ class TestSecondaryRemoved(MockClientTest):
wait_until(lambda: ('c', 3) in c.secondaries, 'discover host "c"')
# C is removed.
c.mock_ismaster_hosts.remove('c:3')
c.mock_hello_hosts.remove('c:3')
wait_until(lambda: set([('b', 2)]) == c.secondaries,
'update list of secondaries')
@ -156,10 +156,10 @@ class TestSecondaryAdded(MockClientTest):
# C is added.
c.mock_members.append('c:3')
c.mock_ismaster_hosts.append('c:3')
c.mock_hello_hosts.append('c:3')
c.close()
c.db.command('ismaster')
c.db.command('ping')
self.assertEqual(c.address, ('a', 1))
@ -181,7 +181,7 @@ class TestSecondaryAdded(MockClientTest):
# C is added.
c.mock_members.append('c:3')
c.mock_ismaster_hosts.append('c:3')
c.mock_hello_hosts.append('c:3')
wait_until(lambda: set([('b', 2), ('c', 3)]) == c.secondaries,
'discover the new secondary')

View File

@ -197,7 +197,7 @@ def create_test(scenario_def):
source_address = clean_node(source)
topology.on_change(ServerDescription(
address=source_address,
ismaster=Hello(response),
hello=Hello(response),
round_trip_time=0))
expected_results = phase['outcome']['events']
@ -326,10 +326,10 @@ class TestSdamMonitoring(IntegrationTest):
def test_network_error_publishes_events(self):
self._test_app_error({'closeConnection': True}, ConnectionFailure)
# In 4.4+, NotMaster errors from failCommand don't cause SDAM state
# In 4.4+, not primary errors from failCommand don't cause SDAM state
# changes because topologyVersion is not incremented.
@client_context.require_version_max(4, 3)
def test_not_master_error_publishes_events(self):
def test_not_primary_error_publishes_events(self):
self._test_app_error({'errorCode': 10107, 'closeConnection': False,
'errorLabels': ['RetryableWriteError']},
NotPrimaryError)

View File

@ -26,8 +26,8 @@ from test import unittest
class TestServer(unittest.TestCase):
def test_repr(self):
ismaster = Hello({'ok': 1})
sd = ServerDescription(('localhost', 27017), ismaster)
hello = Hello({'ok': 1})
sd = ServerDescription(('localhost', 27017), hello)
server = Server(sd, pool=object(), monitor=object())
self.assertTrue('Standalone' in str(server))

View File

@ -21,36 +21,36 @@ sys.path[0:0] = [""]
from bson.objectid import ObjectId
from bson.int64 import Int64
from pymongo.server_type import SERVER_TYPE
from pymongo.hello import Hello
from pymongo.hello import Hello, HelloCompat
from pymongo.server_description import ServerDescription
from test import unittest
address = ('localhost', 27017)
def parse_ismaster_response(doc):
ismaster_response = Hello(doc)
return ServerDescription(address, ismaster_response)
def parse_hello_response(doc):
hello_response = Hello(doc)
return ServerDescription(address, hello_response)
class TestServerDescription(unittest.TestCase):
def test_unknown(self):
# Default, no ismaster_response.
# Default, no hello_response.
s = ServerDescription(address)
self.assertEqual(SERVER_TYPE.Unknown, s.server_type)
self.assertFalse(s.is_writable)
self.assertFalse(s.is_readable)
def test_mongos(self):
s = parse_ismaster_response({'ok': 1, 'msg': 'isdbgrid'})
s = parse_hello_response({'ok': 1, 'msg': 'isdbgrid'})
self.assertEqual(SERVER_TYPE.Mongos, s.server_type)
self.assertEqual('Mongos', s.server_type_name)
self.assertTrue(s.is_writable)
self.assertTrue(s.is_readable)
def test_primary(self):
s = parse_ismaster_response(
{'ok': 1, 'ismaster': True, 'setName': 'rs'})
s = parse_hello_response(
{'ok': 1, HelloCompat.LEGACY_CMD: True, 'setName': 'rs'})
self.assertEqual(SERVER_TYPE.RSPrimary, s.server_type)
self.assertEqual('RSPrimary', s.server_type_name)
@ -58,8 +58,8 @@ class TestServerDescription(unittest.TestCase):
self.assertTrue(s.is_readable)
def test_secondary(self):
s = parse_ismaster_response(
{'ok': 1, 'ismaster': False, 'secondary': True, 'setName': 'rs'})
s = parse_hello_response(
{'ok': 1, HelloCompat.LEGACY_CMD: False, 'secondary': True, 'setName': 'rs'})
self.assertEqual(SERVER_TYPE.RSSecondary, s.server_type)
self.assertEqual('RSSecondary', s.server_type_name)
@ -67,8 +67,8 @@ class TestServerDescription(unittest.TestCase):
self.assertTrue(s.is_readable)
def test_arbiter(self):
s = parse_ismaster_response(
{'ok': 1, 'ismaster': False, 'arbiterOnly': True, 'setName': 'rs'})
s = parse_hello_response(
{'ok': 1, HelloCompat.LEGACY_CMD: False, 'arbiterOnly': True, 'setName': 'rs'})
self.assertEqual(SERVER_TYPE.RSArbiter, s.server_type)
self.assertEqual('RSArbiter', s.server_type_name)
@ -76,15 +76,15 @@ class TestServerDescription(unittest.TestCase):
self.assertFalse(s.is_readable)
def test_other(self):
s = parse_ismaster_response(
{'ok': 1, 'ismaster': False, 'setName': 'rs'})
s = parse_hello_response(
{'ok': 1, HelloCompat.LEGACY_CMD: False, 'setName': 'rs'})
self.assertEqual(SERVER_TYPE.RSOther, s.server_type)
self.assertEqual('RSOther', s.server_type_name)
s = parse_ismaster_response({
s = parse_hello_response({
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'hidden': True,
'setName': 'rs'})
@ -94,7 +94,7 @@ class TestServerDescription(unittest.TestCase):
self.assertFalse(s.is_readable)
def test_ghost(self):
s = parse_ismaster_response({'ok': 1, 'isreplicaset': True})
s = parse_hello_response({'ok': 1, 'isreplicaset': True})
self.assertEqual(SERVER_TYPE.RSGhost, s.server_type)
self.assertEqual('RSGhost', s.server_type_name)
@ -102,9 +102,9 @@ class TestServerDescription(unittest.TestCase):
self.assertFalse(s.is_readable)
def test_fields(self):
s = parse_ismaster_response({
s = parse_hello_response({
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'primary': 'a:27017',
'tags': {'a': 'foo', 'b': 'baz'},
@ -125,35 +125,35 @@ class TestServerDescription(unittest.TestCase):
self.assertEqual(5, s.max_wire_version)
def test_default_max_message_size(self):
s = parse_ismaster_response({
s = parse_hello_response({
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'maxBsonObjectSize': 2})
# Twice max_bson_size.
self.assertEqual(4, s.max_message_size)
def test_standalone(self):
s = parse_ismaster_response({'ok': 1, 'ismaster': True})
s = parse_hello_response({'ok': 1, HelloCompat.LEGACY_CMD: True})
self.assertEqual(SERVER_TYPE.Standalone, s.server_type)
# Mongod started with --slave.
# master-slave replication was removed in MongoDB 4.0.
s = parse_ismaster_response({'ok': 1, 'ismaster': False})
s = parse_hello_response({'ok': 1, HelloCompat.LEGACY_CMD: False})
self.assertEqual(SERVER_TYPE.Standalone, s.server_type)
self.assertTrue(s.is_writable)
self.assertTrue(s.is_readable)
def test_ok_false(self):
s = parse_ismaster_response({'ok': 0, 'ismaster': True})
s = parse_hello_response({'ok': 0, HelloCompat.LEGACY_CMD: True})
self.assertEqual(SERVER_TYPE.Unknown, s.server_type)
self.assertFalse(s.is_writable)
self.assertFalse(s.is_readable)
def test_all_hosts(self):
s = parse_ismaster_response({
s = parse_hello_response({
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'hosts': ['a'],
'passives': ['b:27018'],
'arbiters': ['c']
@ -164,15 +164,15 @@ class TestServerDescription(unittest.TestCase):
sorted(s.all_hosts))
def test_repr(self):
s = parse_ismaster_response({'ok': 1, 'msg': 'isdbgrid'})
s = parse_hello_response({'ok': 1, 'msg': 'isdbgrid'})
self.assertEqual(repr(s),
"<ServerDescription ('localhost', 27017)"
" server_type: Mongos, rtt: None>")
def test_topology_version(self):
topology_version = {'processId': ObjectId(), 'counter': Int64('0')}
s = parse_ismaster_response(
{'ok': 1, 'ismaster': True, 'setName': 'rs',
s = parse_hello_response(
{'ok': 1, HelloCompat.LEGACY_CMD: True, 'setName': 'rs',
'topologyVersion': topology_version})
self.assertEqual(SERVER_TYPE.RSPrimary, s.server_type)
@ -185,8 +185,8 @@ class TestServerDescription(unittest.TestCase):
def test_topology_version_not_present(self):
# No topologyVersion field.
s = parse_ismaster_response(
{'ok': 1, 'ismaster': True, 'setName': 'rs'})
s = parse_hello_response(
{'ok': 1, HelloCompat.LEGACY_CMD: True, 'setName': 'rs'})
self.assertEqual(SERVER_TYPE.RSPrimary, s.server_type)
self.assertEqual(None, s.topology_version)

View File

@ -20,6 +20,7 @@ import sys
from pymongo import MongoClient
from pymongo import ReadPreference
from pymongo.errors import ServerSelectionTimeoutError
from pymongo.hello import HelloCompat
from pymongo.server_selectors import writable_server_selector
from pymongo.settings import TopologySettings
from pymongo.topology import Topology
@ -75,7 +76,7 @@ class TestCustomServerSelectorFunction(IntegrationTest):
# Wait the node list to be fully populated.
def all_hosts_started():
return (len(client.admin.command('isMaster')['hosts']) ==
return (len(client.admin.command(HelloCompat.LEGACY_CMD)['hosts']) ==
len(client._topology._description.readable_servers))
wait_until(all_hosts_started, 'receive heartbeat from all hosts')

View File

@ -1027,7 +1027,7 @@ class TestSessionsNotSupported(IntegrationTest):
class TestClusterTime(IntegrationTest):
def setUp(self):
super(TestClusterTime, self).setUp()
if '$clusterTime' not in client_context.ismaster:
if '$clusterTime' not in client_context.hello:
raise SkipTest('$clusterTime not supported')
def test_cluster_time(self):

View File

@ -26,6 +26,7 @@ from pymongo import MongoClient, ssl_support
from pymongo.errors import (ConfigurationError,
ConnectionFailure,
OperationFailure)
from pymongo.hello import HelloCompat
from pymongo.ssl_support import HAVE_SSL, get_ssl_context, _ssl
from pymongo.write_concern import WriteConcern
from test import (IntegrationTest,
@ -221,7 +222,7 @@ class TestSSL(IntegrationTest):
client = MongoClient(client_context.host, client_context.port,
tlsAllowInvalidCertificates=True,
tlsCertificateKeyFile=CLIENT_PEM)
response = client.admin.command('ismaster')
response = client.admin.command(HelloCompat.LEGACY_CMD)
if 'setName' in response:
client = MongoClient(client_context.pair,
replicaSet=response['setName'],
@ -245,7 +246,7 @@ class TestSSL(IntegrationTest):
tlsCertificateKeyFile=CLIENT_PEM,
tlsAllowInvalidCertificates=False,
tlsCAFile=CA_PEM)
response = client.admin.command('ismaster')
response = client.admin.command(HelloCompat.LEGACY_CMD)
if 'setName' in response:
if response['primary'].split(":")[0] != 'localhost':
raise SkipTest("No hosts in the replicaset for 'localhost'. "
@ -303,7 +304,7 @@ class TestSSL(IntegrationTest):
else:
self.assertFalse(ctx.check_hostname)
response = self.client.admin.command('ismaster')
response = self.client.admin.command(HelloCompat.LEGACY_CMD)
with self.assertRaises(ConnectionFailure):
connected(MongoClient('server',
@ -603,7 +604,7 @@ class TestSSL(IntegrationTest):
tls=True,
tlsCertificateKeyFile=CLIENT_PEM,
tlsCAFile=temp_ca_bundle) as client:
self.assertTrue(client.admin.command('ismaster'))
self.assertTrue(client.admin.command('ping'))
if __name__ == "__main__":

View File

@ -20,6 +20,7 @@ import time
sys.path[0:0] = [""]
from pymongo import monitoring
from pymongo.hello import HelloCompat
from test import (client_context,
IntegrationTest,
@ -45,17 +46,17 @@ class TestStreamingProtocol(IntegrationTest):
address = client.address
listener.reset()
fail_ismaster = {
fail_hello = {
'configureFailPoint': 'failCommand',
'mode': {'times': 4},
'data': {
'failCommands': ['isMaster', 'hello'],
'failCommands': [HelloCompat.LEGACY_CMD, 'hello'],
'closeConnection': False,
'errorCode': 10107,
'appName': 'failingHeartbeatTest',
},
}
with self.fail_point(fail_ismaster):
with self.fail_point(fail_hello):
def _marked_unknown(event):
return (event.server_address == address
and not event.new_description.is_server_type_known)
@ -83,21 +84,21 @@ class TestStreamingProtocol(IntegrationTest):
listener = ServerEventListener()
hb_listener = HeartbeatEventListener()
# On Windows, RTT can actually be 0.0 because time.time() only has
# 1-15 millisecond resolution. We need to delay the initial isMaster
# 1-15 millisecond resolution. We need to delay the initial hello
# to ensure that RTT is never zero.
name = 'streamingRttTest'
delay_ismaster = {
delay_hello = {
'configureFailPoint': 'failCommand',
'mode': {'times': 1000},
'data': {
'failCommands': ['isMaster', 'hello'],
'failCommands': [HelloCompat.LEGACY_CMD, 'hello'],
'blockConnection': True,
'blockTimeMS': 20,
# This can be uncommented after SERVER-49220 is fixed.
# 'appName': name,
},
}
with self.fail_point(delay_ismaster):
with self.fail_point(delay_hello):
client = rs_or_single_client(
event_listeners=[listener, hb_listener],
heartbeatFrequencyMS=500,
@ -107,9 +108,9 @@ class TestStreamingProtocol(IntegrationTest):
client.admin.command('ping')
address = client.address
delay_ismaster['data']['blockTimeMS'] = 500
delay_ismaster['data']['appName'] = name
with self.fail_point(delay_ismaster):
delay_hello['data']['blockTimeMS'] = 500
delay_hello['data']['appName'] = name
with self.fail_point(delay_hello):
def rtt_exceeds_250_ms():
# XXX: Add a public TopologyDescription getter to MongoClient?
topology = client._topology
@ -135,15 +136,15 @@ class TestStreamingProtocol(IntegrationTest):
def test_monitor_waits_after_server_check_error(self):
# This test implements:
# https://github.com/mongodb/specifications/blob/6c5b2ac/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst#monitors-sleep-at-least-minheartbeatfreqencyms-between-checks
fail_ismaster = {
fail_hello = {
'mode': {'times': 5},
'data': {
'failCommands': ['isMaster', 'hello'],
'failCommands': [HelloCompat.LEGACY_CMD, 'hello'],
'errorCode': 1234,
'appName': 'SDAMMinHeartbeatFrequencyTest',
},
}
with self.fail_point(fail_ismaster):
with self.fail_point(fail_hello):
start = time.time()
client = single_client(
appName='SDAMMinHeartbeatFrequencyTest',
@ -161,7 +162,7 @@ class TestStreamingProtocol(IntegrationTest):
# 1502ms: failed monitor handshake, 4
# 2002ms: failed monitor handshake, 5
# 2502ms: monitor handshake succeeds
# 2503ms: run awaitable isMaster
# 2503ms: run awaitable hello
# 2504ms: application handshake succeeds
# 2505ms: ping command succeeds
self.assertGreaterEqual(duration, 2)
@ -186,7 +187,7 @@ class TestStreamingProtocol(IntegrationTest):
fail_heartbeat = {
'mode': {'times': 2},
'data': {
'failCommands': ['isMaster', 'hello'],
'failCommands': [HelloCompat.LEGACY_CMD, 'hello'],
'closeConnection': True,
'appName': 'heartbeatEventAwaitedFlag',
},

View File

@ -21,24 +21,24 @@ sys.path[0:0] = [""]
from bson.objectid import ObjectId
from pymongo import common
from pymongo.read_preferences import ReadPreference, Secondary
from pymongo.server_type import SERVER_TYPE
from pymongo.topology import (_ErrorContext,
Topology)
from pymongo.topology_description import TOPOLOGY_TYPE
from pymongo.errors import (AutoReconnect,
ConfigurationError,
ConnectionFailure)
from pymongo.hello import Hello
from pymongo.hello import Hello, HelloCompat
from pymongo.monitor import Monitor
from pymongo.pool import PoolOptions
from pymongo.read_preferences import ReadPreference, Secondary
from pymongo.server_description import ServerDescription
from pymongo.server_selectors import (any_server_selector,
writable_server_selector)
from pymongo.server_type import SERVER_TYPE
from pymongo.settings import TopologySettings
from pymongo.topology import (_ErrorContext,
Topology)
from pymongo.topology_description import TOPOLOGY_TYPE
from test import client_knobs, unittest
from test.utils import MockPool, wait_until
from test.pymongo_mocks import DummyMonitor
from test.utils import MockPool, wait_until
class SetNameDiscoverySettings(TopologySettings):
@ -65,9 +65,9 @@ def create_mock_topology(
return t
def got_ismaster(topology, server_address, ismaster_response):
def got_hello(topology, server_address, hello_response):
server_description = ServerDescription(
server_address, Hello(ismaster_response), 0)
server_address, Hello(hello_response), 0)
topology.on_change(server_description)
@ -119,23 +119,23 @@ class TestTopologyConfiguration(TopologyTest):
self.assertEqual(1, monitor._pool.opts.connect_timeout)
self.assertEqual(1, monitor._pool.opts.socket_timeout)
# The monitor, not its pool, is responsible for calling ismaster.
# The monitor, not its pool, is responsible for calling hello.
self.assertFalse(monitor._pool.handshake)
class TestSingleServerTopology(TopologyTest):
def test_direct_connection(self):
for server_type, ismaster_response in [
for server_type, hello_response in [
(SERVER_TYPE.RSPrimary, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'hosts': ['a'],
'setName': 'rs',
'maxWireVersion': 6}),
(SERVER_TYPE.RSSecondary, {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'hosts': ['a'],
'setName': 'rs',
@ -143,13 +143,13 @@ class TestSingleServerTopology(TopologyTest):
(SERVER_TYPE.Mongos, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'msg': 'isdbgrid',
'maxWireVersion': 6}),
(SERVER_TYPE.RSArbiter, {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'arbiterOnly': True,
'hosts': ['a'],
'setName': 'rs',
@ -157,7 +157,7 @@ class TestSingleServerTopology(TopologyTest):
(SERVER_TYPE.Standalone, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'maxWireVersion': 6}),
# A "slave" in a master-slave deployment.
@ -165,7 +165,7 @@ class TestSingleServerTopology(TopologyTest):
# 4.0.
(SERVER_TYPE.Standalone, {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'maxWireVersion': 6}),
]:
t = create_mock_topology()
@ -176,7 +176,7 @@ class TestSingleServerTopology(TopologyTest):
t.select_servers(any_server_selector,
server_selection_timeout=0)
got_ismaster(t, address, ismaster_response)
got_hello(t, address, hello_response)
# Topology type never changes.
self.assertEqual(TOPOLOGY_TYPE.Single, t.description.topology_type)
@ -267,15 +267,15 @@ class TestSingleServerTopology(TopologyTest):
class TestMultiServerTopology(TopologyTest):
def test_readable_writable(self):
t = create_mock_topology(replica_set_name='rs')
got_ismaster(t, ('a', 27017), {
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'b']})
got_ismaster(t, ('b', 27017), {
got_hello(t, ('b', 27017), {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'rs',
'hosts': ['a', 'b']})
@ -291,16 +291,16 @@ class TestMultiServerTopology(TopologyTest):
Secondary(tag_sets=[{'tag': 'exists'}])))
t = create_mock_topology(replica_set_name='rs')
got_ismaster(t, ('a', 27017), {
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': False,
'setName': 'rs',
'hosts': ['a', 'b']})
got_ismaster(t, ('b', 27017), {
got_hello(t, ('b', 27017), {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'rs',
'hosts': ['a', 'b']})
@ -316,15 +316,15 @@ class TestMultiServerTopology(TopologyTest):
Secondary(tag_sets=[{'tag': 'exists'}])))
t = create_mock_topology(replica_set_name='rs')
got_ismaster(t, ('a', 27017), {
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'b']})
got_ismaster(t, ('b', 27017), {
got_hello(t, ('b', 27017), {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'rs',
'hosts': ['a', 'b'],
@ -342,15 +342,15 @@ class TestMultiServerTopology(TopologyTest):
def test_close(self):
t = create_mock_topology(replica_set_name='rs')
got_ismaster(t, ('a', 27017), {
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'b']})
got_ismaster(t, ('b', 27017), {
got_hello(t, ('b', 27017), {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'rs',
'hosts': ['a', 'b']})
@ -372,10 +372,10 @@ class TestMultiServerTopology(TopologyTest):
self.assertEqual(TOPOLOGY_TYPE.ReplicaSetNoPrimary,
t.description.topology_type)
# A closed topology should not be updated when receiving an isMaster.
got_ismaster(t, ('a', 27017), {
# A closed topology should not be updated when receiving a hello.
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'b', 'c']})
@ -392,15 +392,15 @@ class TestMultiServerTopology(TopologyTest):
def test_handle_error(self):
t = create_mock_topology(replica_set_name='rs')
got_ismaster(t, ('a', 27017), {
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'b']})
got_ismaster(t, ('b', 27017), {
got_hello(t, ('b', 27017), {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'rs',
'hosts': ['a', 'b']})
@ -413,9 +413,9 @@ class TestMultiServerTopology(TopologyTest):
self.assertEqual(TOPOLOGY_TYPE.ReplicaSetNoPrimary,
t.description.topology_type)
got_ismaster(t, ('a', 27017), {
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'b']})
@ -453,9 +453,9 @@ class TestMultiServerTopology(TopologyTest):
self.assertEqual(t.description.topology_type,
TOPOLOGY_TYPE.ReplicaSetNoPrimary)
t.open()
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a']})
@ -465,9 +465,9 @@ class TestMultiServerTopology(TopologyTest):
# Another response from the primary. Tests the code that processes
# primary response when topology type is already ReplicaSetWithPrimary.
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a']})
@ -489,9 +489,9 @@ class TestMultiServerTopology(TopologyTest):
self.assertEqual(t.description.topology_type,
TOPOLOGY_TYPE.ReplicaSetNoPrimary)
t.open()
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'rs',
'hosts': ['a']})
@ -504,9 +504,9 @@ class TestMultiServerTopology(TopologyTest):
t = create_mock_topology(replica_set_name='rs')
t.description.check_compatible() # No error.
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a']})
@ -515,9 +515,9 @@ class TestMultiServerTopology(TopologyTest):
self.assertEqual(server.description.min_wire_version, 0)
self.assertEqual(server.description.max_wire_version, 0)
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a'],
'minWireVersion': 1,
@ -527,9 +527,9 @@ class TestMultiServerTopology(TopologyTest):
self.assertEqual(server.description.max_wire_version, 5)
# Incompatible.
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a'],
'minWireVersion': 21,
@ -548,9 +548,9 @@ class TestMultiServerTopology(TopologyTest):
self.fail('No error with incompatible wire version')
# Incompatible.
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a'],
'minWireVersion': 0,
@ -576,17 +576,17 @@ class TestMultiServerTopology(TopologyTest):
s = t.select_server(writable_server_selector)
return s.description.max_write_batch_size
got_ismaster(t, ('a', 27017), {
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'b'],
'maxWireVersion': 6,
'maxWriteBatchSize': 1})
got_ismaster(t, ('b', 27017), {
got_hello(t, ('b', 27017), {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'rs',
'hosts': ['a', 'b'],
@ -597,9 +597,9 @@ class TestMultiServerTopology(TopologyTest):
self.assertEqual(1, write_batch_size())
# b becomes primary.
got_ismaster(t, ('b', 27017), {
got_hello(t, ('b', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'b'],
'maxWireVersion': 6,
@ -610,9 +610,9 @@ class TestMultiServerTopology(TopologyTest):
def test_topology_repr(self):
t = create_mock_topology(replica_set_name='rs')
self.addCleanup(t.close)
got_ismaster(t, ('a', 27017), {
got_hello(t, ('a', 27017), {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a', 'c', 'b']})
self.assertEqual(
@ -632,7 +632,7 @@ class TestMultiServerTopology(TopologyTest):
t = create_mock_topology(seeds=['a'])
mock_lb_response = {'ok': 1, 'msg': 'isdbgrid',
'serviceId': ObjectId(), 'maxWireVersion': 13}
got_ismaster(t, ('a', 27017), mock_lb_response)
got_hello(t, ('a', 27017), mock_lb_response)
sds = t.description.server_descriptions()
self.assertIn(('a', 27017), sds)
self.assertEqual(sds[('a', 27017)].server_type_name, 'LoadBalancer')
@ -641,84 +641,84 @@ class TestMultiServerTopology(TopologyTest):
# Load balancers are removed from a topology with multiple seeds.
t = create_mock_topology(seeds=['a', 'b'])
got_ismaster(t, ('a', 27017), mock_lb_response)
got_hello(t, ('a', 27017), mock_lb_response)
self.assertNotIn(('a', 27017), t.description.server_descriptions())
self.assertEqual(t.description.topology_type_name, 'Unknown')
def wait_for_master(topology):
def wait_for_primary(topology):
"""Wait for a Topology to discover a writable server.
If the monitor is currently calling ismaster, a blocking call to
If the monitor is currently calling hello, a blocking call to
select_server from this thread can trigger a spurious wake of the monitor
thread. In applications this is harmless but it would break some tests,
so we pass server_selection_timeout=0 and poll instead.
"""
def get_master():
def get_primary():
try:
return topology.select_server(writable_server_selector, 0)
except ConnectionFailure:
return None
return wait_until(get_master, 'find master')
return wait_until(get_primary, 'find primary')
class TestTopologyErrors(TopologyTest):
# Errors when calling ismaster.
# Errors when calling hello.
def test_pool_reset(self):
# ismaster succeeds at first, then always raises socket error.
ismaster_count = [0]
# hello succeeds at first, then always raises socket error.
hello_count = [0]
class TestMonitor(Monitor):
def _check_with_socket(self, *args, **kwargs):
ismaster_count[0] += 1
if ismaster_count[0] == 1:
hello_count[0] += 1
if hello_count[0] == 1:
return Hello({'ok': 1, 'maxWireVersion': 6}), 0
else:
raise AutoReconnect('mock monitor error')
t = create_mock_topology(monitor_class=TestMonitor)
self.addCleanup(t.close)
server = wait_for_master(t)
self.assertEqual(1, ismaster_count[0])
server = wait_for_primary(t)
self.assertEqual(1, hello_count[0])
generation = server.pool.gen.get_overall()
# Pool is reset by ismaster failure.
# Pool is reset by hello failure.
t.request_check_all()
self.assertNotEqual(generation, server.pool.gen.get_overall())
def test_ismaster_retry(self):
# ismaster succeeds at first, then raises socket error, then succeeds.
ismaster_count = [0]
def test_hello_retry(self):
# hello succeeds at first, then raises socket error, then succeeds.
hello_count = [0]
class TestMonitor(Monitor):
def _check_with_socket(self, *args, **kwargs):
ismaster_count[0] += 1
if ismaster_count[0] in (1, 3):
hello_count[0] += 1
if hello_count[0] in (1, 3):
return Hello({'ok': 1, 'maxWireVersion': 6}), 0
else:
raise AutoReconnect(
'mock monitor error #%s' % (ismaster_count[0],))
'mock monitor error #%s' % (hello_count[0],))
t = create_mock_topology(monitor_class=TestMonitor)
self.addCleanup(t.close)
server = wait_for_master(t)
self.assertEqual(1, ismaster_count[0])
server = wait_for_primary(t)
self.assertEqual(1, hello_count[0])
self.assertEqual(SERVER_TYPE.Standalone,
server.description.server_type)
# Second ismaster call, server is marked Unknown, then the monitor
# immediately runs a retry (third ismaster).
# Second hello call, server is marked Unknown, then the monitor
# immediately runs a retry (third hello).
t.request_check_all()
# The third ismaster call (the immediate retry) happens sometime soon
# The third hello call (the immediate retry) happens sometime soon
# after the failed check triggered by request_check_all. Wait until
# the server becomes known again.
server = t.select_server(writable_server_selector, 0.250)
self.assertEqual(SERVER_TYPE.Standalone,
server.description.server_type)
self.assertEqual(3, ismaster_count[0])
self.assertEqual(3, hello_count[0])
def test_internal_monitor_error(self):
exception = AssertionError('internal error')
@ -743,9 +743,9 @@ class TestServerSelectionErrors(TopologyTest):
def test_no_primary(self):
t = create_mock_topology(replica_set_name='rs')
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'rs',
'hosts': ['a']})
@ -758,9 +758,9 @@ class TestServerSelectionErrors(TopologyTest):
def test_no_secondary(self):
t = create_mock_topology(replica_set_name='rs')
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': True,
HelloCompat.LEGACY_CMD: True,
'setName': 'rs',
'hosts': ['a']})
@ -777,9 +777,9 @@ class TestServerSelectionErrors(TopologyTest):
def test_bad_replica_set_name(self):
t = create_mock_topology(replica_set_name='rs')
got_ismaster(t, address, {
got_hello(t, address, {
'ok': 1,
'ismaster': False,
HelloCompat.LEGACY_CMD: False,
'secondary': True,
'setName': 'wrong',
'hosts': ['a']})
@ -790,8 +790,8 @@ class TestServerSelectionErrors(TopologyTest):
def test_multiple_standalones(self):
# Standalones are removed from a topology with multiple seeds.
t = create_mock_topology(seeds=['a', 'b'])
got_ismaster(t, ('a', 27017), {'ok': 1})
got_ismaster(t, ('b', 27017), {'ok': 1})
got_hello(t, ('a', 27017), {'ok': 1})
got_hello(t, ('b', 27017), {'ok': 1})
self.assertMessage('No servers available', t)
def test_no_mongoses(self):
@ -799,11 +799,11 @@ class TestServerSelectionErrors(TopologyTest):
t = create_mock_topology(seeds=['a', 'b'])
# Discover a mongos and change topology type to Sharded.
got_ismaster(t, ('a', 27017), {'ok': 1, 'msg': 'isdbgrid'})
got_hello(t, ('a', 27017), {'ok': 1, 'msg': 'isdbgrid'})
# Oops, both servers are standalone now. Remove them.
got_ismaster(t, ('a', 27017), {'ok': 1})
got_ismaster(t, ('b', 27017), {'ok': 1})
got_hello(t, ('a', 27017), {'ok': 1})
got_hello(t, ('b', 27017), {'ok': 1})
self.assertMessage('No mongoses available', t)

View File

@ -38,6 +38,7 @@ from pymongo import (MongoClient,
monitoring, operations, read_preferences)
from pymongo.collection import ReturnDocument
from pymongo.errors import ConfigurationError, OperationFailure
from pymongo.hello import HelloCompat
from pymongo.monitoring import _SENSITIVE_COMMANDS
from pymongo.pool import _CancellationContext, _PoolGeneration
from pymongo.read_concern import ReadConcern
@ -576,19 +577,19 @@ def ensure_all_connected(client):
Depending on the use-case, the caller may need to clear any event listeners
that are configured on the client.
"""
ismaster = client.admin.command("isMaster")
if 'setName' not in ismaster:
hello = client.admin.command(HelloCompat.LEGACY_CMD)
if 'setName' not in hello:
raise ConfigurationError("cluster is not a replica set")
target_host_list = set(ismaster['hosts'])
connected_host_list = set([ismaster['me']])
target_host_list = set(hello['hosts'])
connected_host_list = set([hello['me']])
admindb = client.get_database('admin')
# Run isMaster until we have connected to each host at least once.
# Run hello until we have connected to each host at least once.
while connected_host_list != target_host_list:
ismaster = admindb.command("isMaster",
hello = admindb.command(HelloCompat.LEGACY_CMD,
read_preference=ReadPreference.SECONDARY)
connected_host_list.update([ismaster["me"]])
connected_host_list.update([hello["me"]])
def one(s):
@ -715,10 +716,10 @@ def joinall(threads):
def connected(client):
"""Convenience to wait for a newly-constructed client to connect."""
with warnings.catch_warnings():
# Ignore warning that "ismaster" is always routed to primary even
# Ignore warning that ping is always routed to primary even
# if client's read preference isn't PRIMARY.
warnings.simplefilter("ignore", UserWarning)
client.admin.command('ismaster') # Force connection.
client.admin.command('ping') # Force connection.
return client
@ -760,7 +761,7 @@ def repl_set_step_down(client, **kwargs):
client.admin.command(cmd)
def is_mongos(client):
res = client.admin.command('ismaster')
res = client.admin.command(HelloCompat.LEGACY_CMD)
return res.get('msg', '') == 'isdbgrid'

View File

@ -23,7 +23,7 @@ sys.path[0:0] = [""]
from bson import json_util
from pymongo.common import clean_node, HEARTBEAT_FREQUENCY
from pymongo.errors import AutoReconnect, ConfigurationError
from pymongo.hello import Hello
from pymongo.hello import Hello, HelloCompat
from pymongo.server_description import ServerDescription
from pymongo.settings import TopologySettings
from pymongo.server_selectors import writable_server_selector
@ -62,30 +62,30 @@ def make_server_description(server, hosts):
if server_type in ("Unknown", "PossiblePrimary"):
return ServerDescription(clean_node(server['address']), Hello({}))
ismaster_response = {'ok': True, 'hosts': hosts}
hello_response = {'ok': True, 'hosts': hosts}
if server_type != "Standalone" and server_type != "Mongos":
ismaster_response['setName'] = "rs"
hello_response['setName'] = "rs"
if server_type == "RSPrimary":
ismaster_response['ismaster'] = True
hello_response[HelloCompat.LEGACY_CMD] = True
elif server_type == "RSSecondary":
ismaster_response['secondary'] = True
hello_response['secondary'] = True
elif server_type == "Mongos":
ismaster_response['msg'] = 'isdbgrid'
hello_response['msg'] = 'isdbgrid'
ismaster_response['lastWrite'] = {
hello_response['lastWrite'] = {
'lastWriteDate': make_last_write_date(server)
}
for field in 'maxWireVersion', 'tags', 'idleWritePeriodMillis':
if field in server:
ismaster_response[field] = server[field]
hello_response[field] = server[field]
ismaster_response.setdefault('maxWireVersion', 6)
hello_response.setdefault('maxWireVersion', 6)
# Sets _last_update_time to now.
sd = ServerDescription(clean_node(server['address']),
Hello(ismaster_response),
Hello(hello_response),
round_trip_time=server['avg_rtt_ms'] / 1000.0)
if 'lastUpdateTime' in server: