From ad4315134c58c0df5bf5249d2288ddcc5ee98f65 Mon Sep 17 00:00:00 2001 From: Bernie Hackett Date: Fri, 10 Sep 2021 16:57:16 -0700 Subject: [PATCH] PYTHON-2803 Get rid of most uses of 'master' This change also resolves PYTHON-2848 for MongoDB 4.0. --- doc/migrate-to-pymongo3.rst | 4 +- pymongo/auth.py | 4 +- pymongo/common.py | 8 +- pymongo/hello.py | 1 + pymongo/helpers.py | 11 +- pymongo/message.py | 3 +- pymongo/mongo_client.py | 4 +- pymongo/monitor.py | 24 +-- pymongo/monitoring.py | 3 +- pymongo/network.py | 2 +- pymongo/pool.py | 46 ++-- pymongo/read_preferences.py | 2 +- pymongo/server_description.py | 48 ++--- pymongo/topology.py | 10 +- pymongo/topology_description.py | 12 +- pymongo/uri_parser.py | 2 +- test/__init__.py | 35 +-- test/atlas/test_connection.py | 2 +- test/mod_wsgi_test/mod_wsgi_test.wsgi | 3 +- test/ocsp/test_ocsp.py | 2 +- test/performance/perf_test.py | 2 +- test/pymongo_mocks.py | 34 +-- test/test_auth.py | 13 +- test/test_bulk.py | 4 +- test/test_client.py | 21 +- test/test_cmap.py | 12 +- test/test_collection.py | 2 +- ...nnections_survive_primary_stepdown_spec.py | 4 +- test/test_discovery_and_monitoring.py | 26 +-- test/test_encryption.py | 6 +- test/test_errors.py | 2 +- test/test_heartbeat_monitoring.py | 4 +- test/test_mongos_load_balancing.py | 14 +- test/test_monitoring.py | 36 ++-- test/test_pooling.py | 2 +- test/test_replica_set_reconfig.py | 10 +- test/test_sdam_monitoring_spec.py | 6 +- test/test_server.py | 4 +- test/test_server_description.py | 62 +++--- test/test_server_selection.py | 3 +- test/test_session.py | 2 +- test/test_ssl.py | 9 +- test/test_streaming_protocol.py | 31 +-- test/test_topology.py | 204 +++++++++--------- test/utils.py | 21 +- test/utils_selection_tests.py | 20 +- 46 files changed, 396 insertions(+), 384 deletions(-) diff --git a/doc/migrate-to-pymongo3.rst b/doc/migrate-to-pymongo3.rst index c1a4c490a..633d0a7ab 100644 --- a/doc/migrate-to-pymongo3.rst +++ b/doc/migrate-to-pymongo3.rst @@ -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 diff --git a/pymongo/auth.py b/pymongo/auth.py index 9861e7717..bfec7ab60 100644 --- a/pymongo/auth.py +++ b/pymongo/auth.py @@ -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) diff --git a/pymongo/common.py b/pymongo/common.py index 90dba0195..f1a5389b3 100644 --- a/pymongo/common.py +++ b/pymongo/common.py @@ -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 diff --git a/pymongo/hello.py b/pymongo/hello.py index 290f27dcc..0ad06e961 100644 --- a/pymongo/hello.py +++ b/pymongo/hello.py @@ -25,6 +25,7 @@ class HelloCompat: LEGACY_CMD = 'ismaster' PRIMARY = 'isWritablePrimary' LEGACY_PRIMARY = 'ismaster' + LEGACY_ERROR = 'not master' def _get_server_type(doc): diff --git a/pymongo/helpers.py b/pymongo/helpers.py index 1d8005ac3..86d1e9f48 100644 --- a/pymongo/helpers.py +++ b/pymongo/helpers.py @@ -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 diff --git a/pymongo/message.py b/pymongo/message.py index 22effb1b2..a30975db9 100644 --- a/pymongo/message.py +++ b/pymongo/message.py @@ -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"), diff --git a/pymongo/mongo_client.py b/pymongo/mongo_client.py index 549389f84..64e3e2e4a 100644 --- a/pymongo/mongo_client.py +++ b/pymongo/mongo_client.py @@ -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") diff --git a/pymongo/monitor.py b/pymongo/monitor.py index df673b720..d13d337f8 100644 --- a/pymongo/monitor.py +++ b/pymongo/monitor.py @@ -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 diff --git a/pymongo/monitoring.py b/pymongo/monitoring.py index 147be2a46..325ec0554 100644 --- a/pymongo/monitoring.py +++ b/pymongo/monitoring.py @@ -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 diff --git a/pymongo/network.py b/pymongo/network.py index cc83d9057..7ec6540dd 100644 --- a/pymongo/network.py +++ b/pymongo/network.py @@ -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: diff --git a/pymongo/pool.py b/pymongo/pool.py index bce21f721..52c57c7f0 100644 --- a/pymongo/pool.py +++ b/pymongo/pool.py @@ -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) diff --git a/pymongo/read_preferences.py b/pymongo/read_preferences.py index 3c07c16ba..c60240822 100644 --- a/pymongo/read_preferences.py +++ b/pymongo/read_preferences.py @@ -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 diff --git a/pymongo/server_description.py b/pymongo/server_description.py index 1c64a8fd5..2cbf6d63c 100644 --- a/pymongo/server_description.py +++ b/pymongo/server_description.py @@ -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 diff --git a/pymongo/topology.py b/pymongo/topology.py index 15247771c..8a2818b5c 100644 --- a/pymongo/topology.py +++ b/pymongo/topology.py @@ -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() diff --git a/pymongo/topology_description.py b/pymongo/topology_description.py index 4282c4ff3..bf29586fa 100644 --- a/pymongo/topology_description.py +++ b/pymongo/topology_description.py @@ -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 diff --git a/pymongo/uri_parser.py b/pymongo/uri_parser.py index 60eb9cba8..cf5cf64f5 100644 --- a/pymongo/uri_parser.py +++ b/pymongo/uri_parser.py @@ -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 diff --git a/test/__init__.py b/test/__init__.py index 6d99f6ec5..388caf715 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -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): diff --git a/test/atlas/test_connection.py b/test/atlas/test_connection.py index ef2a83369..1ad84068e 100644 --- a/test/atlas/test_connection.py +++ b/test/atlas/test_connection.py @@ -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({}) diff --git a/test/mod_wsgi_test/mod_wsgi_test.wsgi b/test/mod_wsgi_test/mod_wsgi_test.wsgi index 9b435b1ed..1fa4e7435 100644 --- a/test/mod_wsgi_test/mod_wsgi_test.wsgi +++ b/test/mod_wsgi_test/mod_wsgi_test.wsgi @@ -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) diff --git a/test/ocsp/test_ocsp.py b/test/ocsp/test_ocsp.py index 4b91c3b93..07197e73b 100644 --- a/test/ocsp/test_ocsp.py +++ b/test/ocsp/test_ocsp.py @@ -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): diff --git a/test/performance/perf_test.py b/test/performance/perf_test.py index 9f6e268df..d84e67aca 100644 --- a/test/performance/perf_test.py +++ b/test/performance/perf_test.py @@ -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): diff --git a/test/pymongo_mocks.py b/test/pymongo_mocks.py index 511560b59..8b1ece8ad 100644 --- a/test/pymongo_mocks.py +++ b/test/pymongo_mocks.py @@ -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 diff --git a/test/test_auth.py b/test/test_auth.py index f7b13a1cf..52159e4f3 100644 --- a/test/test_auth.py +++ b/test/test_auth.py @@ -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 diff --git a/test/test_bulk.py b/test/test_bulk.py index 9834da55c..f0cb52912 100644 --- a/test/test_bulk.py +++ b/test/test_bulk.py @@ -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 diff --git a/test/test_client.py b/test/test_client.py index 3584329fd..8ac1f86be 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -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) diff --git a/test/test_cmap.py b/test/test_cmap.py index 3a7b70852..38966eb66 100644 --- a/test/test_cmap.py +++ b/test/test_cmap.py @@ -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) diff --git a/test/test_collection.py b/test/test_collection.py index ba6d2b544..2795b4742 100644 --- a/test/test_collection.py +++ b/test/test_collection.py @@ -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( diff --git a/test/test_connections_survive_primary_stepdown_spec.py b/test/test_connections_survive_primary_stepdown_spec.py index 6ed16fa50..894b14bec 100644 --- a/test/test_connections_survive_primary_stepdown_spec.py +++ b/test/test_connections_survive_primary_stepdown_spec.py @@ -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) diff --git a/test/test_discovery_and_monitoring.py b/test/test_discovery_and_monitoring.py index 0b3ac6da5..c26c0df30 100644 --- a/test/test_discovery_and_monitoring.py +++ b/test/test_discovery_and_monitoring.py @@ -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( diff --git a/test/test_encryption.py b/test/test_encryption.py index efd12c372..adab7221d 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -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): diff --git a/test/test_errors.py b/test/test_errors.py index aec7bf478..b70b0d529 100644 --- a/test/test_errors.py +++ b/test/test_errors.py @@ -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) diff --git a/test/test_heartbeat_monitoring.py b/test/test_heartbeat_monitoring.py index bd926f772..6941e6bd8 100644 --- a/test/test_heartbeat_monitoring.py +++ b/test/test_heartbeat_monitoring.py @@ -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 diff --git a/test/test_mongos_load_balancing.py b/test/test_mongos_load_balancing.py index 96a7e9f6b..e67b1186e 100644 --- a/test/test_mongos_load_balancing.py +++ b/test/test_mongos_load_balancing.py @@ -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') diff --git a/test/test_monitoring.py b/test/test_monitoring.py index 3ff9f04a8..c9f4e5ae7 100644 --- a/test/test_monitoring.py +++ b/test/test_monitoring.py @@ -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), "") + "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), "") 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), "") def test_server_heartbeat_event_repr(self): diff --git a/test/test_pooling.py b/test/test_pooling.py index d5f4f09a9..becbacc1e 100644 --- a/test/test_pooling.py +++ b/test/test_pooling.py @@ -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. diff --git a/test/test_replica_set_reconfig.py b/test/test_replica_set_reconfig.py index 62dc0ac0a..a25d7af16 100644 --- a/test/test_replica_set_reconfig.py +++ b/test/test_replica_set_reconfig.py @@ -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') diff --git a/test/test_sdam_monitoring_spec.py b/test/test_sdam_monitoring_spec.py index cb168bb2b..25b0064a0 100644 --- a/test/test_sdam_monitoring_spec.py +++ b/test/test_sdam_monitoring_spec.py @@ -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) diff --git a/test/test_server.py b/test/test_server.py index ca3ce76cd..e4996d2e0 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -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)) diff --git a/test/test_server_description.py b/test/test_server_description.py index 7abb8b9c1..23d6c8f37 100644 --- a/test/test_server_description.py +++ b/test/test_server_description.py @@ -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), "") 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) diff --git a/test/test_server_selection.py b/test/test_server_selection.py index fc8f64316..1e4165246 100644 --- a/test/test_server_selection.py +++ b/test/test_server_selection.py @@ -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') diff --git a/test/test_session.py b/test/test_session.py index 7a78ca72d..fc1f8382f 100644 --- a/test/test_session.py +++ b/test/test_session.py @@ -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): diff --git a/test/test_ssl.py b/test/test_ssl.py index d6055cafb..7c3cc4bac 100644 --- a/test/test_ssl.py +++ b/test/test_ssl.py @@ -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__": diff --git a/test/test_streaming_protocol.py b/test/test_streaming_protocol.py index f2b2c7cca..4715fbfee 100644 --- a/test/test_streaming_protocol.py +++ b/test/test_streaming_protocol.py @@ -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', }, diff --git a/test/test_topology.py b/test/test_topology.py index 151383799..f2b23eb74 100644 --- a/test/test_topology.py +++ b/test/test_topology.py @@ -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) diff --git a/test/utils.py b/test/utils.py index fa1c8511b..43528b31d 100644 --- a/test/utils.py +++ b/test/utils.py @@ -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' diff --git a/test/utils_selection_tests.py b/test/utils_selection_tests.py index 4037705ae..6de0b7303 100644 --- a/test/utils_selection_tests.py +++ b/test/utils_selection_tests.py @@ -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: