sensu-plugins-mongodb-mrtrotl 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE +22 -0
- data/README.md +27 -0
- data/bin/check-mongodb-metric.rb +144 -0
- data/bin/check-mongodb-query-count.rb +267 -0
- data/bin/check-mongodb.py +1644 -0
- data/bin/check-mongodb.rb +5 -0
- data/bin/metrics-mongodb-replication.rb +254 -0
- data/bin/metrics-mongodb.rb +133 -0
- data/lib/bson/__init__.py +1347 -0
- data/lib/bson/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/_helpers.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/binary.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/code.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/codec_options.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/dbref.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/decimal128.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/int64.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/json_util.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/max_key.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/min_key.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/objectid.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/raw_bson.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/regex.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/son.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/timestamp.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/tz_util.cpython-310.pyc +0 -0
- data/lib/bson/_cbson.cpython-310-x86_64-linux-gnu.so +0 -0
- data/lib/bson/_helpers.py +41 -0
- data/lib/bson/binary.py +364 -0
- data/lib/bson/code.py +101 -0
- data/lib/bson/codec_options.py +414 -0
- data/lib/bson/codec_options.pyi +100 -0
- data/lib/bson/dbref.py +133 -0
- data/lib/bson/decimal128.py +314 -0
- data/lib/bson/errors.py +35 -0
- data/lib/bson/int64.py +39 -0
- data/lib/bson/json_util.py +874 -0
- data/lib/bson/max_key.py +55 -0
- data/lib/bson/min_key.py +55 -0
- data/lib/bson/objectid.py +286 -0
- data/lib/bson/py.typed +2 -0
- data/lib/bson/raw_bson.py +175 -0
- data/lib/bson/regex.py +135 -0
- data/lib/bson/son.py +208 -0
- data/lib/bson/timestamp.py +124 -0
- data/lib/bson/tz_util.py +52 -0
- data/lib/gridfs/__init__.py +1015 -0
- data/lib/gridfs/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/gridfs/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/gridfs/__pycache__/grid_file.cpython-310.pyc +0 -0
- data/lib/gridfs/errors.py +33 -0
- data/lib/gridfs/grid_file.py +907 -0
- data/lib/gridfs/py.typed +2 -0
- data/lib/pymongo/__init__.py +185 -0
- data/lib/pymongo/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/_csot.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/aggregation.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/auth.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/auth_aws.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/bulk.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/change_stream.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/client_options.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/client_session.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/collation.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/collection.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/command_cursor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/common.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/compression_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/cursor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/daemon.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/database.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/driver_info.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/encryption.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/encryption_options.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/event_loggers.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/hello.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/helpers.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/max_staleness_selectors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/message.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/mongo_client.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/monitor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/monitoring.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/network.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ocsp_cache.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ocsp_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/operations.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/periodic_executor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/pool.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/pyopenssl_context.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/read_concern.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/read_preferences.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/response.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/results.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/saslprep.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_api.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_description.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_selectors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_type.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/settings.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/socket_checker.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/srv_resolver.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ssl_context.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ssl_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/topology.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/topology_description.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/typings.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/uri_parser.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/write_concern.cpython-310.pyc +0 -0
- data/lib/pymongo/_cmessage.cpython-310-x86_64-linux-gnu.so +0 -0
- data/lib/pymongo/_csot.py +118 -0
- data/lib/pymongo/aggregation.py +229 -0
- data/lib/pymongo/auth.py +549 -0
- data/lib/pymongo/auth_aws.py +94 -0
- data/lib/pymongo/bulk.py +513 -0
- data/lib/pymongo/change_stream.py +457 -0
- data/lib/pymongo/client_options.py +302 -0
- data/lib/pymongo/client_session.py +1112 -0
- data/lib/pymongo/collation.py +224 -0
- data/lib/pymongo/collection.py +3204 -0
- data/lib/pymongo/command_cursor.py +353 -0
- data/lib/pymongo/common.py +984 -0
- data/lib/pymongo/compression_support.py +149 -0
- data/lib/pymongo/cursor.py +1345 -0
- data/lib/pymongo/daemon.py +141 -0
- data/lib/pymongo/database.py +1202 -0
- data/lib/pymongo/driver_info.py +42 -0
- data/lib/pymongo/encryption.py +884 -0
- data/lib/pymongo/encryption_options.py +221 -0
- data/lib/pymongo/errors.py +365 -0
- data/lib/pymongo/event_loggers.py +221 -0
- data/lib/pymongo/hello.py +219 -0
- data/lib/pymongo/helpers.py +259 -0
- data/lib/pymongo/max_staleness_selectors.py +114 -0
- data/lib/pymongo/message.py +1440 -0
- data/lib/pymongo/mongo_client.py +2144 -0
- data/lib/pymongo/monitor.py +440 -0
- data/lib/pymongo/monitoring.py +1801 -0
- data/lib/pymongo/network.py +311 -0
- data/lib/pymongo/ocsp_cache.py +87 -0
- data/lib/pymongo/ocsp_support.py +372 -0
- data/lib/pymongo/operations.py +507 -0
- data/lib/pymongo/periodic_executor.py +183 -0
- data/lib/pymongo/pool.py +1660 -0
- data/lib/pymongo/py.typed +2 -0
- data/lib/pymongo/pyopenssl_context.py +383 -0
- data/lib/pymongo/read_concern.py +75 -0
- data/lib/pymongo/read_preferences.py +609 -0
- data/lib/pymongo/response.py +109 -0
- data/lib/pymongo/results.py +217 -0
- data/lib/pymongo/saslprep.py +113 -0
- data/lib/pymongo/server.py +247 -0
- data/lib/pymongo/server_api.py +170 -0
- data/lib/pymongo/server_description.py +285 -0
- data/lib/pymongo/server_selectors.py +153 -0
- data/lib/pymongo/server_type.py +32 -0
- data/lib/pymongo/settings.py +159 -0
- data/lib/pymongo/socket_checker.py +104 -0
- data/lib/pymongo/srv_resolver.py +126 -0
- data/lib/pymongo/ssl_context.py +39 -0
- data/lib/pymongo/ssl_support.py +99 -0
- data/lib/pymongo/topology.py +890 -0
- data/lib/pymongo/topology_description.py +639 -0
- data/lib/pymongo/typings.py +39 -0
- data/lib/pymongo/uri_parser.py +624 -0
- data/lib/pymongo/write_concern.py +129 -0
- data/lib/pymongo-4.2.0.dist-info/INSTALLER +1 -0
- data/lib/pymongo-4.2.0.dist-info/LICENSE +201 -0
- data/lib/pymongo-4.2.0.dist-info/METADATA +250 -0
- data/lib/pymongo-4.2.0.dist-info/RECORD +167 -0
- data/lib/pymongo-4.2.0.dist-info/REQUESTED +0 -0
- data/lib/pymongo-4.2.0.dist-info/WHEEL +6 -0
- data/lib/pymongo-4.2.0.dist-info/top_level.txt +3 -0
- data/lib/sensu-plugins-mongodb/metrics.rb +391 -0
- data/lib/sensu-plugins-mongodb/version.rb +9 -0
- data/lib/sensu-plugins-mongodb.rb +1 -0
- metadata +407 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
# Copyright 2014-present MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
4
|
+
# may not use this file except in compliance with the License. You
|
5
|
+
# may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
# implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
"""Represent MongoClient's configuration."""
|
16
|
+
|
17
|
+
import threading
|
18
|
+
import traceback
|
19
|
+
|
20
|
+
from bson.objectid import ObjectId
|
21
|
+
from pymongo import common, monitor, pool
|
22
|
+
from pymongo.common import LOCAL_THRESHOLD_MS, SERVER_SELECTION_TIMEOUT
|
23
|
+
from pymongo.errors import ConfigurationError
|
24
|
+
from pymongo.pool import PoolOptions
|
25
|
+
from pymongo.server_description import ServerDescription
|
26
|
+
from pymongo.topology_description import TOPOLOGY_TYPE
|
27
|
+
|
28
|
+
|
29
|
+
class TopologySettings(object):
|
30
|
+
def __init__(
|
31
|
+
self,
|
32
|
+
seeds=None,
|
33
|
+
replica_set_name=None,
|
34
|
+
pool_class=None,
|
35
|
+
pool_options=None,
|
36
|
+
monitor_class=None,
|
37
|
+
condition_class=None,
|
38
|
+
local_threshold_ms=LOCAL_THRESHOLD_MS,
|
39
|
+
server_selection_timeout=SERVER_SELECTION_TIMEOUT,
|
40
|
+
heartbeat_frequency=common.HEARTBEAT_FREQUENCY,
|
41
|
+
server_selector=None,
|
42
|
+
fqdn=None,
|
43
|
+
direct_connection=False,
|
44
|
+
load_balanced=None,
|
45
|
+
srv_service_name=common.SRV_SERVICE_NAME,
|
46
|
+
srv_max_hosts=0,
|
47
|
+
):
|
48
|
+
"""Represent MongoClient's configuration.
|
49
|
+
|
50
|
+
Take a list of (host, port) pairs and optional replica set name.
|
51
|
+
"""
|
52
|
+
if heartbeat_frequency < common.MIN_HEARTBEAT_INTERVAL:
|
53
|
+
raise ConfigurationError(
|
54
|
+
"heartbeatFrequencyMS cannot be less than %d"
|
55
|
+
% (common.MIN_HEARTBEAT_INTERVAL * 1000,)
|
56
|
+
)
|
57
|
+
|
58
|
+
self._seeds = seeds or [("localhost", 27017)]
|
59
|
+
self._replica_set_name = replica_set_name
|
60
|
+
self._pool_class = pool_class or pool.Pool
|
61
|
+
self._pool_options = pool_options or PoolOptions()
|
62
|
+
self._monitor_class = monitor_class or monitor.Monitor
|
63
|
+
self._condition_class = condition_class or threading.Condition
|
64
|
+
self._local_threshold_ms = local_threshold_ms
|
65
|
+
self._server_selection_timeout = server_selection_timeout
|
66
|
+
self._server_selector = server_selector
|
67
|
+
self._fqdn = fqdn
|
68
|
+
self._heartbeat_frequency = heartbeat_frequency
|
69
|
+
self._direct = direct_connection
|
70
|
+
self._load_balanced = load_balanced
|
71
|
+
self._srv_service_name = srv_service_name
|
72
|
+
self._srv_max_hosts = srv_max_hosts or 0
|
73
|
+
|
74
|
+
self._topology_id = ObjectId()
|
75
|
+
# Store the allocation traceback to catch unclosed clients in the
|
76
|
+
# test suite.
|
77
|
+
self._stack = "".join(traceback.format_stack())
|
78
|
+
|
79
|
+
@property
|
80
|
+
def seeds(self):
|
81
|
+
"""List of server addresses."""
|
82
|
+
return self._seeds
|
83
|
+
|
84
|
+
@property
|
85
|
+
def replica_set_name(self):
|
86
|
+
return self._replica_set_name
|
87
|
+
|
88
|
+
@property
|
89
|
+
def pool_class(self):
|
90
|
+
return self._pool_class
|
91
|
+
|
92
|
+
@property
|
93
|
+
def pool_options(self):
|
94
|
+
return self._pool_options
|
95
|
+
|
96
|
+
@property
|
97
|
+
def monitor_class(self):
|
98
|
+
return self._monitor_class
|
99
|
+
|
100
|
+
@property
|
101
|
+
def condition_class(self):
|
102
|
+
return self._condition_class
|
103
|
+
|
104
|
+
@property
|
105
|
+
def local_threshold_ms(self):
|
106
|
+
return self._local_threshold_ms
|
107
|
+
|
108
|
+
@property
|
109
|
+
def server_selection_timeout(self):
|
110
|
+
return self._server_selection_timeout
|
111
|
+
|
112
|
+
@property
|
113
|
+
def server_selector(self):
|
114
|
+
return self._server_selector
|
115
|
+
|
116
|
+
@property
|
117
|
+
def heartbeat_frequency(self):
|
118
|
+
return self._heartbeat_frequency
|
119
|
+
|
120
|
+
@property
|
121
|
+
def fqdn(self):
|
122
|
+
return self._fqdn
|
123
|
+
|
124
|
+
@property
|
125
|
+
def direct(self):
|
126
|
+
"""Connect directly to a single server, or use a set of servers?
|
127
|
+
|
128
|
+
True if there is one seed and no replica_set_name.
|
129
|
+
"""
|
130
|
+
return self._direct
|
131
|
+
|
132
|
+
@property
|
133
|
+
def load_balanced(self):
|
134
|
+
"""True if the client was configured to connect to a load balancer."""
|
135
|
+
return self._load_balanced
|
136
|
+
|
137
|
+
@property
|
138
|
+
def srv_service_name(self):
|
139
|
+
"""The srvServiceName."""
|
140
|
+
return self._srv_service_name
|
141
|
+
|
142
|
+
@property
|
143
|
+
def srv_max_hosts(self):
|
144
|
+
"""The srvMaxHosts."""
|
145
|
+
return self._srv_max_hosts
|
146
|
+
|
147
|
+
def get_topology_type(self):
|
148
|
+
if self.load_balanced:
|
149
|
+
return TOPOLOGY_TYPE.LoadBalanced
|
150
|
+
elif self.direct:
|
151
|
+
return TOPOLOGY_TYPE.Single
|
152
|
+
elif self.replica_set_name is not None:
|
153
|
+
return TOPOLOGY_TYPE.ReplicaSetNoPrimary
|
154
|
+
else:
|
155
|
+
return TOPOLOGY_TYPE.Unknown
|
156
|
+
|
157
|
+
def get_server_descriptions(self):
|
158
|
+
"""Initial dict of (address, ServerDescription) for all seeds."""
|
159
|
+
return dict([(address, ServerDescription(address)) for address in self.seeds])
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Copyright 2020-present MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""Select / poll helper"""
|
16
|
+
|
17
|
+
import errno
|
18
|
+
import select
|
19
|
+
import sys
|
20
|
+
from typing import Any, Optional
|
21
|
+
|
22
|
+
# PYTHON-2320: Jython does not fully support poll on SSL sockets,
|
23
|
+
# https://bugs.jython.org/issue2900
|
24
|
+
_HAVE_POLL = hasattr(select, "poll") and not sys.platform.startswith("java")
|
25
|
+
_SelectError = getattr(select, "error", OSError)
|
26
|
+
|
27
|
+
|
28
|
+
def _errno_from_exception(exc):
|
29
|
+
if hasattr(exc, "errno"):
|
30
|
+
return exc.errno
|
31
|
+
if exc.args:
|
32
|
+
return exc.args[0]
|
33
|
+
return None
|
34
|
+
|
35
|
+
|
36
|
+
class SocketChecker(object):
|
37
|
+
def __init__(self) -> None:
|
38
|
+
self._poller: Optional[select.poll]
|
39
|
+
if _HAVE_POLL:
|
40
|
+
self._poller = select.poll()
|
41
|
+
else:
|
42
|
+
self._poller = None
|
43
|
+
|
44
|
+
def select(
|
45
|
+
self, sock: Any, read: bool = False, write: bool = False, timeout: Optional[float] = 0
|
46
|
+
) -> bool:
|
47
|
+
"""Select for reads or writes with a timeout in seconds (or None).
|
48
|
+
|
49
|
+
Returns True if the socket is readable/writable, False on timeout.
|
50
|
+
"""
|
51
|
+
res: Any
|
52
|
+
while True:
|
53
|
+
try:
|
54
|
+
if self._poller:
|
55
|
+
mask = select.POLLERR | select.POLLHUP
|
56
|
+
if read:
|
57
|
+
mask = mask | select.POLLIN | select.POLLPRI
|
58
|
+
if write:
|
59
|
+
mask = mask | select.POLLOUT
|
60
|
+
self._poller.register(sock, mask)
|
61
|
+
try:
|
62
|
+
# poll() timeout is in milliseconds. select()
|
63
|
+
# timeout is in seconds.
|
64
|
+
timeout_ = None if timeout is None else timeout * 1000
|
65
|
+
res = self._poller.poll(timeout_)
|
66
|
+
# poll returns a possibly-empty list containing
|
67
|
+
# (fd, event) 2-tuples for the descriptors that have
|
68
|
+
# events or errors to report. Return True if the list
|
69
|
+
# is not empty.
|
70
|
+
return bool(res)
|
71
|
+
finally:
|
72
|
+
self._poller.unregister(sock)
|
73
|
+
else:
|
74
|
+
rlist = [sock] if read else []
|
75
|
+
wlist = [sock] if write else []
|
76
|
+
res = select.select(rlist, wlist, [sock], timeout)
|
77
|
+
# select returns a 3-tuple of lists of objects that are
|
78
|
+
# ready: subsets of the first three arguments. Return
|
79
|
+
# True if any of the lists are not empty.
|
80
|
+
return any(res)
|
81
|
+
except (_SelectError, IOError) as exc: # type: ignore
|
82
|
+
if _errno_from_exception(exc) in (errno.EINTR, errno.EAGAIN):
|
83
|
+
continue
|
84
|
+
raise
|
85
|
+
|
86
|
+
def socket_closed(self, sock: Any) -> bool:
|
87
|
+
"""Return True if we know socket has been closed, False otherwise."""
|
88
|
+
try:
|
89
|
+
return self.select(sock, read=True)
|
90
|
+
except (RuntimeError, KeyError):
|
91
|
+
# RuntimeError is raised during a concurrent poll. KeyError
|
92
|
+
# is raised by unregister if the socket is not in the poller.
|
93
|
+
# These errors should not be possible since we protect the
|
94
|
+
# poller with a mutex.
|
95
|
+
raise
|
96
|
+
except ValueError:
|
97
|
+
# ValueError is raised by register/unregister/select if the
|
98
|
+
# socket file descriptor is negative or outside the range for
|
99
|
+
# select (> 1023).
|
100
|
+
return True
|
101
|
+
except Exception:
|
102
|
+
# Any other exceptions should be attributed to a closed
|
103
|
+
# or invalid socket.
|
104
|
+
return True
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Copyright 2019-present MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
4
|
+
# may not use this file except in compliance with the License. You
|
5
|
+
# may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
# implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
"""Support for resolving hosts and options from mongodb+srv:// URIs."""
|
16
|
+
|
17
|
+
import ipaddress
|
18
|
+
import random
|
19
|
+
|
20
|
+
try:
|
21
|
+
from dns import resolver
|
22
|
+
|
23
|
+
_HAVE_DNSPYTHON = True
|
24
|
+
except ImportError:
|
25
|
+
_HAVE_DNSPYTHON = False
|
26
|
+
|
27
|
+
from pymongo.common import CONNECT_TIMEOUT
|
28
|
+
from pymongo.errors import ConfigurationError
|
29
|
+
|
30
|
+
|
31
|
+
# dnspython can return bytes or str from various parts
|
32
|
+
# of its API depending on version. We always want str.
|
33
|
+
def maybe_decode(text):
|
34
|
+
if isinstance(text, bytes):
|
35
|
+
return text.decode()
|
36
|
+
return text
|
37
|
+
|
38
|
+
|
39
|
+
# PYTHON-2667 Lazily call dns.resolver methods for compatibility with eventlet.
|
40
|
+
def _resolve(*args, **kwargs):
|
41
|
+
if hasattr(resolver, "resolve"):
|
42
|
+
# dnspython >= 2
|
43
|
+
return resolver.resolve(*args, **kwargs)
|
44
|
+
# dnspython 1.X
|
45
|
+
return resolver.query(*args, **kwargs)
|
46
|
+
|
47
|
+
|
48
|
+
_INVALID_HOST_MSG = (
|
49
|
+
"Invalid URI host: %s is not a valid hostname for 'mongodb+srv://'. "
|
50
|
+
"Did you mean to use 'mongodb://'?"
|
51
|
+
)
|
52
|
+
|
53
|
+
|
54
|
+
class _SrvResolver(object):
|
55
|
+
def __init__(self, fqdn, connect_timeout, srv_service_name, srv_max_hosts=0):
|
56
|
+
self.__fqdn = fqdn
|
57
|
+
self.__srv = srv_service_name
|
58
|
+
self.__connect_timeout = connect_timeout or CONNECT_TIMEOUT
|
59
|
+
self.__srv_max_hosts = srv_max_hosts or 0
|
60
|
+
# Validate the fully qualified domain name.
|
61
|
+
try:
|
62
|
+
ipaddress.ip_address(fqdn)
|
63
|
+
raise ConfigurationError(_INVALID_HOST_MSG % ("an IP address",))
|
64
|
+
except ValueError:
|
65
|
+
pass
|
66
|
+
|
67
|
+
try:
|
68
|
+
self.__plist = self.__fqdn.split(".")[1:]
|
69
|
+
except Exception:
|
70
|
+
raise ConfigurationError(_INVALID_HOST_MSG % (fqdn,))
|
71
|
+
self.__slen = len(self.__plist)
|
72
|
+
if self.__slen < 2:
|
73
|
+
raise ConfigurationError(_INVALID_HOST_MSG % (fqdn,))
|
74
|
+
|
75
|
+
def get_options(self):
|
76
|
+
try:
|
77
|
+
results = _resolve(self.__fqdn, "TXT", lifetime=self.__connect_timeout)
|
78
|
+
except (resolver.NoAnswer, resolver.NXDOMAIN):
|
79
|
+
# No TXT records
|
80
|
+
return None
|
81
|
+
except Exception as exc:
|
82
|
+
raise ConfigurationError(str(exc))
|
83
|
+
if len(results) > 1:
|
84
|
+
raise ConfigurationError("Only one TXT record is supported")
|
85
|
+
return (b"&".join([b"".join(res.strings) for res in results])).decode("utf-8")
|
86
|
+
|
87
|
+
def _resolve_uri(self, encapsulate_errors):
|
88
|
+
try:
|
89
|
+
results = _resolve(
|
90
|
+
"_" + self.__srv + "._tcp." + self.__fqdn, "SRV", lifetime=self.__connect_timeout
|
91
|
+
)
|
92
|
+
except Exception as exc:
|
93
|
+
if not encapsulate_errors:
|
94
|
+
# Raise the original error.
|
95
|
+
raise
|
96
|
+
# Else, raise all errors as ConfigurationError.
|
97
|
+
raise ConfigurationError(str(exc))
|
98
|
+
return results
|
99
|
+
|
100
|
+
def _get_srv_response_and_hosts(self, encapsulate_errors):
|
101
|
+
results = self._resolve_uri(encapsulate_errors)
|
102
|
+
|
103
|
+
# Construct address tuples
|
104
|
+
nodes = [
|
105
|
+
(maybe_decode(res.target.to_text(omit_final_dot=True)), res.port) for res in results
|
106
|
+
]
|
107
|
+
|
108
|
+
# Validate hosts
|
109
|
+
for node in nodes:
|
110
|
+
try:
|
111
|
+
nlist = node[0].split(".")[1:][-self.__slen :]
|
112
|
+
except Exception:
|
113
|
+
raise ConfigurationError("Invalid SRV host: %s" % (node[0],))
|
114
|
+
if self.__plist != nlist:
|
115
|
+
raise ConfigurationError("Invalid SRV host: %s" % (node[0],))
|
116
|
+
if self.__srv_max_hosts:
|
117
|
+
nodes = random.sample(nodes, min(self.__srv_max_hosts, len(nodes)))
|
118
|
+
return results, nodes
|
119
|
+
|
120
|
+
def get_hosts(self):
|
121
|
+
_, nodes = self._get_srv_response_and_hosts(True)
|
122
|
+
return nodes
|
123
|
+
|
124
|
+
def get_hosts_and_min_ttl(self):
|
125
|
+
results, nodes = self._get_srv_response_and_hosts(False)
|
126
|
+
return nodes, results.rrset.ttl
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Copyright 2014-present MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
4
|
+
# may not use this file except in compliance with the License. You
|
5
|
+
# may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
# implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
"""A fake SSLContext implementation."""
|
16
|
+
|
17
|
+
import ssl as _ssl
|
18
|
+
|
19
|
+
# PROTOCOL_TLS_CLIENT is Python 3.6+
|
20
|
+
PROTOCOL_SSLv23 = getattr(_ssl, "PROTOCOL_TLS_CLIENT", _ssl.PROTOCOL_SSLv23)
|
21
|
+
OP_NO_SSLv2 = getattr(_ssl, "OP_NO_SSLv2", 0)
|
22
|
+
OP_NO_SSLv3 = getattr(_ssl, "OP_NO_SSLv3", 0)
|
23
|
+
OP_NO_COMPRESSION = getattr(_ssl, "OP_NO_COMPRESSION", 0)
|
24
|
+
# Python 3.7+, OpenSSL 1.1.0h+
|
25
|
+
OP_NO_RENEGOTIATION = getattr(_ssl, "OP_NO_RENEGOTIATION", 0)
|
26
|
+
|
27
|
+
HAS_SNI = getattr(_ssl, "HAS_SNI", False)
|
28
|
+
IS_PYOPENSSL = False
|
29
|
+
|
30
|
+
# Errors raised by SSL sockets when in non-blocking mode.
|
31
|
+
BLOCKING_IO_ERRORS = (_ssl.SSLWantReadError, _ssl.SSLWantWriteError)
|
32
|
+
|
33
|
+
# Base Exception class
|
34
|
+
SSLError = _ssl.SSLError
|
35
|
+
|
36
|
+
from ssl import SSLContext # noqa: F401,E402
|
37
|
+
|
38
|
+
if hasattr(_ssl, "VERIFY_CRL_CHECK_LEAF"):
|
39
|
+
from ssl import VERIFY_CRL_CHECK_LEAF # noqa: F401
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Copyright 2014-present MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
4
|
+
# may not use this file except in compliance with the License. You
|
5
|
+
# may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
# implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
"""Support for SSL in PyMongo."""
|
16
|
+
|
17
|
+
from pymongo.errors import ConfigurationError
|
18
|
+
|
19
|
+
HAVE_SSL = True
|
20
|
+
|
21
|
+
try:
|
22
|
+
import pymongo.pyopenssl_context as _ssl
|
23
|
+
except ImportError:
|
24
|
+
try:
|
25
|
+
import pymongo.ssl_context as _ssl # type: ignore[no-redef]
|
26
|
+
except ImportError:
|
27
|
+
HAVE_SSL = False
|
28
|
+
|
29
|
+
|
30
|
+
if HAVE_SSL:
|
31
|
+
# Note: The validate* functions below deal with users passing
|
32
|
+
# CPython ssl module constants to configure certificate verification
|
33
|
+
# at a high level. This is legacy behavior, but requires us to
|
34
|
+
# import the ssl module even if we're only using it for this purpose.
|
35
|
+
import ssl as _stdlibssl # noqa
|
36
|
+
from ssl import CERT_NONE, CERT_REQUIRED
|
37
|
+
|
38
|
+
HAS_SNI = _ssl.HAS_SNI
|
39
|
+
IPADDR_SAFE = True
|
40
|
+
SSLError = _ssl.SSLError
|
41
|
+
BLOCKING_IO_ERRORS = _ssl.BLOCKING_IO_ERRORS
|
42
|
+
|
43
|
+
def get_ssl_context(
|
44
|
+
certfile,
|
45
|
+
passphrase,
|
46
|
+
ca_certs,
|
47
|
+
crlfile,
|
48
|
+
allow_invalid_certificates,
|
49
|
+
allow_invalid_hostnames,
|
50
|
+
disable_ocsp_endpoint_check,
|
51
|
+
):
|
52
|
+
"""Create and return an SSLContext object."""
|
53
|
+
verify_mode = CERT_NONE if allow_invalid_certificates else CERT_REQUIRED
|
54
|
+
ctx = _ssl.SSLContext(_ssl.PROTOCOL_SSLv23)
|
55
|
+
if verify_mode != CERT_NONE:
|
56
|
+
ctx.check_hostname = not allow_invalid_hostnames
|
57
|
+
else:
|
58
|
+
ctx.check_hostname = False
|
59
|
+
if hasattr(ctx, "check_ocsp_endpoint"):
|
60
|
+
ctx.check_ocsp_endpoint = not disable_ocsp_endpoint_check
|
61
|
+
if hasattr(ctx, "options"):
|
62
|
+
# Explicitly disable SSLv2, SSLv3 and TLS compression. Note that
|
63
|
+
# up to date versions of MongoDB 2.4 and above already disable
|
64
|
+
# SSLv2 and SSLv3, python disables SSLv2 by default in >= 2.7.7
|
65
|
+
# and >= 3.3.4 and SSLv3 in >= 3.4.3.
|
66
|
+
ctx.options |= _ssl.OP_NO_SSLv2
|
67
|
+
ctx.options |= _ssl.OP_NO_SSLv3
|
68
|
+
ctx.options |= _ssl.OP_NO_COMPRESSION
|
69
|
+
ctx.options |= _ssl.OP_NO_RENEGOTIATION
|
70
|
+
if certfile is not None:
|
71
|
+
try:
|
72
|
+
ctx.load_cert_chain(certfile, None, passphrase)
|
73
|
+
except _ssl.SSLError as exc:
|
74
|
+
raise ConfigurationError("Private key doesn't match certificate: %s" % (exc,))
|
75
|
+
if crlfile is not None:
|
76
|
+
if _ssl.IS_PYOPENSSL:
|
77
|
+
raise ConfigurationError("tlsCRLFile cannot be used with PyOpenSSL")
|
78
|
+
# Match the server's behavior.
|
79
|
+
setattr(ctx, "verify_flags", getattr(_ssl, "VERIFY_CRL_CHECK_LEAF", 0)) # noqa
|
80
|
+
ctx.load_verify_locations(crlfile)
|
81
|
+
if ca_certs is not None:
|
82
|
+
ctx.load_verify_locations(ca_certs)
|
83
|
+
elif verify_mode != CERT_NONE:
|
84
|
+
ctx.load_default_certs()
|
85
|
+
ctx.verify_mode = verify_mode
|
86
|
+
return ctx
|
87
|
+
|
88
|
+
else:
|
89
|
+
|
90
|
+
class SSLError(Exception): # type: ignore
|
91
|
+
pass
|
92
|
+
|
93
|
+
HAS_SNI = False
|
94
|
+
IPADDR_SAFE = False
|
95
|
+
BLOCKING_IO_ERRORS = () # type: ignore
|
96
|
+
|
97
|
+
def get_ssl_context(*dummy): # type: ignore
|
98
|
+
"""No ssl module, raise ConfigurationError."""
|
99
|
+
raise ConfigurationError("The ssl module is not available.")
|