PYTHON-2803 Get rid of most uses of 'master'
This change also resolves PYTHON-2848 for MongoDB 4.0.
This commit is contained in:
parent
146179db53
commit
ad4315134c
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ class HelloCompat:
|
||||
LEGACY_CMD = 'ismaster'
|
||||
PRIMARY = 'isWritablePrimary'
|
||||
LEGACY_PRIMARY = 'ismaster'
|
||||
LEGACY_ERROR = 'not master'
|
||||
|
||||
|
||||
def _get_server_type(doc):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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")
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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({})
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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__":
|
||||
|
||||
@ -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',
|
||||
},
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user