sensu-plugins-mongodb-mrtrotl 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +22 -0
  4. data/README.md +27 -0
  5. data/bin/check-mongodb-metric.rb +144 -0
  6. data/bin/check-mongodb-query-count.rb +267 -0
  7. data/bin/check-mongodb.py +1644 -0
  8. data/bin/check-mongodb.rb +5 -0
  9. data/bin/metrics-mongodb-replication.rb +254 -0
  10. data/bin/metrics-mongodb.rb +133 -0
  11. data/lib/bson/__init__.py +1347 -0
  12. data/lib/bson/__pycache__/__init__.cpython-310.pyc +0 -0
  13. data/lib/bson/__pycache__/_helpers.cpython-310.pyc +0 -0
  14. data/lib/bson/__pycache__/binary.cpython-310.pyc +0 -0
  15. data/lib/bson/__pycache__/code.cpython-310.pyc +0 -0
  16. data/lib/bson/__pycache__/codec_options.cpython-310.pyc +0 -0
  17. data/lib/bson/__pycache__/dbref.cpython-310.pyc +0 -0
  18. data/lib/bson/__pycache__/decimal128.cpython-310.pyc +0 -0
  19. data/lib/bson/__pycache__/errors.cpython-310.pyc +0 -0
  20. data/lib/bson/__pycache__/int64.cpython-310.pyc +0 -0
  21. data/lib/bson/__pycache__/json_util.cpython-310.pyc +0 -0
  22. data/lib/bson/__pycache__/max_key.cpython-310.pyc +0 -0
  23. data/lib/bson/__pycache__/min_key.cpython-310.pyc +0 -0
  24. data/lib/bson/__pycache__/objectid.cpython-310.pyc +0 -0
  25. data/lib/bson/__pycache__/raw_bson.cpython-310.pyc +0 -0
  26. data/lib/bson/__pycache__/regex.cpython-310.pyc +0 -0
  27. data/lib/bson/__pycache__/son.cpython-310.pyc +0 -0
  28. data/lib/bson/__pycache__/timestamp.cpython-310.pyc +0 -0
  29. data/lib/bson/__pycache__/tz_util.cpython-310.pyc +0 -0
  30. data/lib/bson/_cbson.cpython-310-x86_64-linux-gnu.so +0 -0
  31. data/lib/bson/_helpers.py +41 -0
  32. data/lib/bson/binary.py +364 -0
  33. data/lib/bson/code.py +101 -0
  34. data/lib/bson/codec_options.py +414 -0
  35. data/lib/bson/codec_options.pyi +100 -0
  36. data/lib/bson/dbref.py +133 -0
  37. data/lib/bson/decimal128.py +314 -0
  38. data/lib/bson/errors.py +35 -0
  39. data/lib/bson/int64.py +39 -0
  40. data/lib/bson/json_util.py +874 -0
  41. data/lib/bson/max_key.py +55 -0
  42. data/lib/bson/min_key.py +55 -0
  43. data/lib/bson/objectid.py +286 -0
  44. data/lib/bson/py.typed +2 -0
  45. data/lib/bson/raw_bson.py +175 -0
  46. data/lib/bson/regex.py +135 -0
  47. data/lib/bson/son.py +208 -0
  48. data/lib/bson/timestamp.py +124 -0
  49. data/lib/bson/tz_util.py +52 -0
  50. data/lib/gridfs/__init__.py +1015 -0
  51. data/lib/gridfs/__pycache__/__init__.cpython-310.pyc +0 -0
  52. data/lib/gridfs/__pycache__/errors.cpython-310.pyc +0 -0
  53. data/lib/gridfs/__pycache__/grid_file.cpython-310.pyc +0 -0
  54. data/lib/gridfs/errors.py +33 -0
  55. data/lib/gridfs/grid_file.py +907 -0
  56. data/lib/gridfs/py.typed +2 -0
  57. data/lib/pymongo/__init__.py +185 -0
  58. data/lib/pymongo/__pycache__/__init__.cpython-310.pyc +0 -0
  59. data/lib/pymongo/__pycache__/_csot.cpython-310.pyc +0 -0
  60. data/lib/pymongo/__pycache__/aggregation.cpython-310.pyc +0 -0
  61. data/lib/pymongo/__pycache__/auth.cpython-310.pyc +0 -0
  62. data/lib/pymongo/__pycache__/auth_aws.cpython-310.pyc +0 -0
  63. data/lib/pymongo/__pycache__/bulk.cpython-310.pyc +0 -0
  64. data/lib/pymongo/__pycache__/change_stream.cpython-310.pyc +0 -0
  65. data/lib/pymongo/__pycache__/client_options.cpython-310.pyc +0 -0
  66. data/lib/pymongo/__pycache__/client_session.cpython-310.pyc +0 -0
  67. data/lib/pymongo/__pycache__/collation.cpython-310.pyc +0 -0
  68. data/lib/pymongo/__pycache__/collection.cpython-310.pyc +0 -0
  69. data/lib/pymongo/__pycache__/command_cursor.cpython-310.pyc +0 -0
  70. data/lib/pymongo/__pycache__/common.cpython-310.pyc +0 -0
  71. data/lib/pymongo/__pycache__/compression_support.cpython-310.pyc +0 -0
  72. data/lib/pymongo/__pycache__/cursor.cpython-310.pyc +0 -0
  73. data/lib/pymongo/__pycache__/daemon.cpython-310.pyc +0 -0
  74. data/lib/pymongo/__pycache__/database.cpython-310.pyc +0 -0
  75. data/lib/pymongo/__pycache__/driver_info.cpython-310.pyc +0 -0
  76. data/lib/pymongo/__pycache__/encryption.cpython-310.pyc +0 -0
  77. data/lib/pymongo/__pycache__/encryption_options.cpython-310.pyc +0 -0
  78. data/lib/pymongo/__pycache__/errors.cpython-310.pyc +0 -0
  79. data/lib/pymongo/__pycache__/event_loggers.cpython-310.pyc +0 -0
  80. data/lib/pymongo/__pycache__/hello.cpython-310.pyc +0 -0
  81. data/lib/pymongo/__pycache__/helpers.cpython-310.pyc +0 -0
  82. data/lib/pymongo/__pycache__/max_staleness_selectors.cpython-310.pyc +0 -0
  83. data/lib/pymongo/__pycache__/message.cpython-310.pyc +0 -0
  84. data/lib/pymongo/__pycache__/mongo_client.cpython-310.pyc +0 -0
  85. data/lib/pymongo/__pycache__/monitor.cpython-310.pyc +0 -0
  86. data/lib/pymongo/__pycache__/monitoring.cpython-310.pyc +0 -0
  87. data/lib/pymongo/__pycache__/network.cpython-310.pyc +0 -0
  88. data/lib/pymongo/__pycache__/ocsp_cache.cpython-310.pyc +0 -0
  89. data/lib/pymongo/__pycache__/ocsp_support.cpython-310.pyc +0 -0
  90. data/lib/pymongo/__pycache__/operations.cpython-310.pyc +0 -0
  91. data/lib/pymongo/__pycache__/periodic_executor.cpython-310.pyc +0 -0
  92. data/lib/pymongo/__pycache__/pool.cpython-310.pyc +0 -0
  93. data/lib/pymongo/__pycache__/pyopenssl_context.cpython-310.pyc +0 -0
  94. data/lib/pymongo/__pycache__/read_concern.cpython-310.pyc +0 -0
  95. data/lib/pymongo/__pycache__/read_preferences.cpython-310.pyc +0 -0
  96. data/lib/pymongo/__pycache__/response.cpython-310.pyc +0 -0
  97. data/lib/pymongo/__pycache__/results.cpython-310.pyc +0 -0
  98. data/lib/pymongo/__pycache__/saslprep.cpython-310.pyc +0 -0
  99. data/lib/pymongo/__pycache__/server.cpython-310.pyc +0 -0
  100. data/lib/pymongo/__pycache__/server_api.cpython-310.pyc +0 -0
  101. data/lib/pymongo/__pycache__/server_description.cpython-310.pyc +0 -0
  102. data/lib/pymongo/__pycache__/server_selectors.cpython-310.pyc +0 -0
  103. data/lib/pymongo/__pycache__/server_type.cpython-310.pyc +0 -0
  104. data/lib/pymongo/__pycache__/settings.cpython-310.pyc +0 -0
  105. data/lib/pymongo/__pycache__/socket_checker.cpython-310.pyc +0 -0
  106. data/lib/pymongo/__pycache__/srv_resolver.cpython-310.pyc +0 -0
  107. data/lib/pymongo/__pycache__/ssl_context.cpython-310.pyc +0 -0
  108. data/lib/pymongo/__pycache__/ssl_support.cpython-310.pyc +0 -0
  109. data/lib/pymongo/__pycache__/topology.cpython-310.pyc +0 -0
  110. data/lib/pymongo/__pycache__/topology_description.cpython-310.pyc +0 -0
  111. data/lib/pymongo/__pycache__/typings.cpython-310.pyc +0 -0
  112. data/lib/pymongo/__pycache__/uri_parser.cpython-310.pyc +0 -0
  113. data/lib/pymongo/__pycache__/write_concern.cpython-310.pyc +0 -0
  114. data/lib/pymongo/_cmessage.cpython-310-x86_64-linux-gnu.so +0 -0
  115. data/lib/pymongo/_csot.py +118 -0
  116. data/lib/pymongo/aggregation.py +229 -0
  117. data/lib/pymongo/auth.py +549 -0
  118. data/lib/pymongo/auth_aws.py +94 -0
  119. data/lib/pymongo/bulk.py +513 -0
  120. data/lib/pymongo/change_stream.py +457 -0
  121. data/lib/pymongo/client_options.py +302 -0
  122. data/lib/pymongo/client_session.py +1112 -0
  123. data/lib/pymongo/collation.py +224 -0
  124. data/lib/pymongo/collection.py +3204 -0
  125. data/lib/pymongo/command_cursor.py +353 -0
  126. data/lib/pymongo/common.py +984 -0
  127. data/lib/pymongo/compression_support.py +149 -0
  128. data/lib/pymongo/cursor.py +1345 -0
  129. data/lib/pymongo/daemon.py +141 -0
  130. data/lib/pymongo/database.py +1202 -0
  131. data/lib/pymongo/driver_info.py +42 -0
  132. data/lib/pymongo/encryption.py +884 -0
  133. data/lib/pymongo/encryption_options.py +221 -0
  134. data/lib/pymongo/errors.py +365 -0
  135. data/lib/pymongo/event_loggers.py +221 -0
  136. data/lib/pymongo/hello.py +219 -0
  137. data/lib/pymongo/helpers.py +259 -0
  138. data/lib/pymongo/max_staleness_selectors.py +114 -0
  139. data/lib/pymongo/message.py +1440 -0
  140. data/lib/pymongo/mongo_client.py +2144 -0
  141. data/lib/pymongo/monitor.py +440 -0
  142. data/lib/pymongo/monitoring.py +1801 -0
  143. data/lib/pymongo/network.py +311 -0
  144. data/lib/pymongo/ocsp_cache.py +87 -0
  145. data/lib/pymongo/ocsp_support.py +372 -0
  146. data/lib/pymongo/operations.py +507 -0
  147. data/lib/pymongo/periodic_executor.py +183 -0
  148. data/lib/pymongo/pool.py +1660 -0
  149. data/lib/pymongo/py.typed +2 -0
  150. data/lib/pymongo/pyopenssl_context.py +383 -0
  151. data/lib/pymongo/read_concern.py +75 -0
  152. data/lib/pymongo/read_preferences.py +609 -0
  153. data/lib/pymongo/response.py +109 -0
  154. data/lib/pymongo/results.py +217 -0
  155. data/lib/pymongo/saslprep.py +113 -0
  156. data/lib/pymongo/server.py +247 -0
  157. data/lib/pymongo/server_api.py +170 -0
  158. data/lib/pymongo/server_description.py +285 -0
  159. data/lib/pymongo/server_selectors.py +153 -0
  160. data/lib/pymongo/server_type.py +32 -0
  161. data/lib/pymongo/settings.py +159 -0
  162. data/lib/pymongo/socket_checker.py +104 -0
  163. data/lib/pymongo/srv_resolver.py +126 -0
  164. data/lib/pymongo/ssl_context.py +39 -0
  165. data/lib/pymongo/ssl_support.py +99 -0
  166. data/lib/pymongo/topology.py +890 -0
  167. data/lib/pymongo/topology_description.py +639 -0
  168. data/lib/pymongo/typings.py +39 -0
  169. data/lib/pymongo/uri_parser.py +624 -0
  170. data/lib/pymongo/write_concern.py +129 -0
  171. data/lib/pymongo-4.2.0.dist-info/INSTALLER +1 -0
  172. data/lib/pymongo-4.2.0.dist-info/LICENSE +201 -0
  173. data/lib/pymongo-4.2.0.dist-info/METADATA +250 -0
  174. data/lib/pymongo-4.2.0.dist-info/RECORD +167 -0
  175. data/lib/pymongo-4.2.0.dist-info/REQUESTED +0 -0
  176. data/lib/pymongo-4.2.0.dist-info/WHEEL +6 -0
  177. data/lib/pymongo-4.2.0.dist-info/top_level.txt +3 -0
  178. data/lib/sensu-plugins-mongodb/metrics.rb +391 -0
  179. data/lib/sensu-plugins-mongodb/version.rb +9 -0
  180. data/lib/sensu-plugins-mongodb.rb +1 -0
  181. metadata +407 -0
@@ -0,0 +1,221 @@
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
+
16
+ """Example event logger classes.
17
+
18
+ .. versionadded:: 3.11
19
+
20
+ These loggers can be registered using :func:`register` or
21
+ :class:`~pymongo.mongo_client.MongoClient`.
22
+
23
+ ``monitoring.register(CommandLogger())``
24
+
25
+ or
26
+
27
+ ``MongoClient(event_listeners=[CommandLogger()])``
28
+ """
29
+ import logging
30
+
31
+ from pymongo import monitoring
32
+
33
+
34
+ class CommandLogger(monitoring.CommandListener):
35
+ """A simple listener that logs command events.
36
+
37
+ Listens for :class:`~pymongo.monitoring.CommandStartedEvent`,
38
+ :class:`~pymongo.monitoring.CommandSucceededEvent` and
39
+ :class:`~pymongo.monitoring.CommandFailedEvent` events and
40
+ logs them at the `INFO` severity level using :mod:`logging`.
41
+ .. versionadded:: 3.11
42
+ """
43
+
44
+ def started(self, event: monitoring.CommandStartedEvent) -> None:
45
+ logging.info(
46
+ f"Command {event.command_name} with request id "
47
+ f"{event.request_id} started on server "
48
+ f"{event.connection_id}"
49
+ )
50
+
51
+ def succeeded(self, event: monitoring.CommandSucceededEvent) -> None:
52
+ logging.info(
53
+ f"Command {event.command_name} with request id "
54
+ f"{event.request_id} on server {event.connection_id} "
55
+ f"succeeded in {event.duration_micros} "
56
+ "microseconds"
57
+ )
58
+
59
+ def failed(self, event: monitoring.CommandFailedEvent) -> None:
60
+ logging.info(
61
+ f"Command {event.command_name} with request id "
62
+ f"{event.request_id} on server {event.connection_id} "
63
+ f"failed in {event.duration_micros} "
64
+ "microseconds"
65
+ )
66
+
67
+
68
+ class ServerLogger(monitoring.ServerListener):
69
+ """A simple listener that logs server discovery events.
70
+
71
+ Listens for :class:`~pymongo.monitoring.ServerOpeningEvent`,
72
+ :class:`~pymongo.monitoring.ServerDescriptionChangedEvent`,
73
+ and :class:`~pymongo.monitoring.ServerClosedEvent`
74
+ events and logs them at the `INFO` severity level using :mod:`logging`.
75
+
76
+ .. versionadded:: 3.11
77
+ """
78
+
79
+ def opened(self, event: monitoring.ServerOpeningEvent) -> None:
80
+ logging.info(f"Server {event.server_address} added to topology {event.topology_id}")
81
+
82
+ def description_changed(self, event: monitoring.ServerDescriptionChangedEvent) -> None:
83
+ previous_server_type = event.previous_description.server_type
84
+ new_server_type = event.new_description.server_type
85
+ if new_server_type != previous_server_type:
86
+ # server_type_name was added in PyMongo 3.4
87
+ logging.info(
88
+ f"Server {event.server_address} changed type from "
89
+ f"{event.previous_description.server_type_name} to "
90
+ f"{event.new_description.server_type_name}"
91
+ )
92
+
93
+ def closed(self, event: monitoring.ServerClosedEvent) -> None:
94
+ logging.warning(f"Server {event.server_address} removed from topology {event.topology_id}")
95
+
96
+
97
+ class HeartbeatLogger(monitoring.ServerHeartbeatListener):
98
+ """A simple listener that logs server heartbeat events.
99
+
100
+ Listens for :class:`~pymongo.monitoring.ServerHeartbeatStartedEvent`,
101
+ :class:`~pymongo.monitoring.ServerHeartbeatSucceededEvent`,
102
+ and :class:`~pymongo.monitoring.ServerHeartbeatFailedEvent`
103
+ events and logs them at the `INFO` severity level using :mod:`logging`.
104
+
105
+ .. versionadded:: 3.11
106
+ """
107
+
108
+ def started(self, event: monitoring.ServerHeartbeatStartedEvent) -> None:
109
+ logging.info(f"Heartbeat sent to server {event.connection_id}")
110
+
111
+ def succeeded(self, event: monitoring.ServerHeartbeatSucceededEvent) -> None:
112
+ # The reply.document attribute was added in PyMongo 3.4.
113
+ logging.info(
114
+ f"Heartbeat to server {event.connection_id} "
115
+ "succeeded with reply "
116
+ f"{event.reply.document}"
117
+ )
118
+
119
+ def failed(self, event: monitoring.ServerHeartbeatFailedEvent) -> None:
120
+ logging.warning(
121
+ f"Heartbeat to server {event.connection_id} failed with error {event.reply}"
122
+ )
123
+
124
+
125
+ class TopologyLogger(monitoring.TopologyListener):
126
+ """A simple listener that logs server topology events.
127
+
128
+ Listens for :class:`~pymongo.monitoring.TopologyOpenedEvent`,
129
+ :class:`~pymongo.monitoring.TopologyDescriptionChangedEvent`,
130
+ and :class:`~pymongo.monitoring.TopologyClosedEvent`
131
+ events and logs them at the `INFO` severity level using :mod:`logging`.
132
+
133
+ .. versionadded:: 3.11
134
+ """
135
+
136
+ def opened(self, event: monitoring.TopologyOpenedEvent) -> None:
137
+ logging.info(f"Topology with id {event.topology_id} opened")
138
+
139
+ def description_changed(self, event: monitoring.TopologyDescriptionChangedEvent) -> None:
140
+ logging.info(f"Topology description updated for topology id {event.topology_id}")
141
+ previous_topology_type = event.previous_description.topology_type
142
+ new_topology_type = event.new_description.topology_type
143
+ if new_topology_type != previous_topology_type:
144
+ # topology_type_name was added in PyMongo 3.4
145
+ logging.info(
146
+ f"Topology {event.topology_id} changed type from "
147
+ f"{event.previous_description.topology_type_name} to "
148
+ f"{event.new_description.topology_type_name}"
149
+ )
150
+ # The has_writable_server and has_readable_server methods
151
+ # were added in PyMongo 3.4.
152
+ if not event.new_description.has_writable_server():
153
+ logging.warning("No writable servers available.")
154
+ if not event.new_description.has_readable_server():
155
+ logging.warning("No readable servers available.")
156
+
157
+ def closed(self, event: monitoring.TopologyClosedEvent) -> None:
158
+ logging.info(f"Topology with id {event.topology_id} closed")
159
+
160
+
161
+ class ConnectionPoolLogger(monitoring.ConnectionPoolListener):
162
+ """A simple listener that logs server connection pool events.
163
+
164
+ Listens for :class:`~pymongo.monitoring.PoolCreatedEvent`,
165
+ :class:`~pymongo.monitoring.PoolClearedEvent`,
166
+ :class:`~pymongo.monitoring.PoolClosedEvent`,
167
+ :~pymongo.monitoring.class:`ConnectionCreatedEvent`,
168
+ :class:`~pymongo.monitoring.ConnectionReadyEvent`,
169
+ :class:`~pymongo.monitoring.ConnectionClosedEvent`,
170
+ :class:`~pymongo.monitoring.ConnectionCheckOutStartedEvent`,
171
+ :class:`~pymongo.monitoring.ConnectionCheckOutFailedEvent`,
172
+ :class:`~pymongo.monitoring.ConnectionCheckedOutEvent`,
173
+ and :class:`~pymongo.monitoring.ConnectionCheckedInEvent`
174
+ events and logs them at the `INFO` severity level using :mod:`logging`.
175
+
176
+ .. versionadded:: 3.11
177
+ """
178
+
179
+ def pool_created(self, event: monitoring.PoolCreatedEvent) -> None:
180
+ logging.info(f"[pool {event.address}] pool created")
181
+
182
+ def pool_ready(self, event):
183
+ logging.info(f"[pool {event.address}] pool ready")
184
+
185
+ def pool_cleared(self, event: monitoring.PoolClearedEvent) -> None:
186
+ logging.info(f"[pool {event.address}] pool cleared")
187
+
188
+ def pool_closed(self, event: monitoring.PoolClosedEvent) -> None:
189
+ logging.info(f"[pool {event.address}] pool closed")
190
+
191
+ def connection_created(self, event: monitoring.ConnectionCreatedEvent) -> None:
192
+ logging.info(f"[pool {event.address}][conn #{event.connection_id}] connection created")
193
+
194
+ def connection_ready(self, event: monitoring.ConnectionReadyEvent) -> None:
195
+ logging.info(
196
+ f"[pool {event.address}][conn #{event.connection_id}] connection setup succeeded"
197
+ )
198
+
199
+ def connection_closed(self, event: monitoring.ConnectionClosedEvent) -> None:
200
+ logging.info(
201
+ f"[pool {event.address}][conn #{event.connection_id}] "
202
+ f'connection closed, reason: "{event.reason}"'
203
+ )
204
+
205
+ def connection_check_out_started(
206
+ self, event: monitoring.ConnectionCheckOutStartedEvent
207
+ ) -> None:
208
+ logging.info(f"[pool {event.address}] connection check out started")
209
+
210
+ def connection_check_out_failed(self, event: monitoring.ConnectionCheckOutFailedEvent) -> None:
211
+ logging.info(f"[pool {event.address}] connection check out failed, reason: {event.reason}")
212
+
213
+ def connection_checked_out(self, event: monitoring.ConnectionCheckedOutEvent) -> None:
214
+ logging.info(
215
+ f"[pool {event.address}][conn #{event.connection_id}] connection checked out of pool"
216
+ )
217
+
218
+ def connection_checked_in(self, event: monitoring.ConnectionCheckedInEvent) -> None:
219
+ logging.info(
220
+ f"[pool {event.address}][conn #{event.connection_id}] connection checked into pool"
221
+ )
@@ -0,0 +1,219 @@
1
+ # Copyright 2021-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
+ """Helpers for the 'hello' and legacy hello commands."""
16
+
17
+ import copy
18
+ import datetime
19
+ import itertools
20
+ from typing import Any, Generic, List, Mapping, Optional, Set, Tuple
21
+
22
+ from bson.objectid import ObjectId
23
+ from pymongo import common
24
+ from pymongo.server_type import SERVER_TYPE
25
+ from pymongo.typings import _DocumentType
26
+
27
+
28
+ class HelloCompat:
29
+ CMD = "hello"
30
+ LEGACY_CMD = "ismaster"
31
+ PRIMARY = "isWritablePrimary"
32
+ LEGACY_PRIMARY = "ismaster"
33
+ LEGACY_ERROR = "not master"
34
+
35
+
36
+ def _get_server_type(doc):
37
+ """Determine the server type from a hello response."""
38
+ if not doc.get("ok"):
39
+ return SERVER_TYPE.Unknown
40
+
41
+ if doc.get("serviceId"):
42
+ return SERVER_TYPE.LoadBalancer
43
+ elif doc.get("isreplicaset"):
44
+ return SERVER_TYPE.RSGhost
45
+ elif doc.get("setName"):
46
+ if doc.get("hidden"):
47
+ return SERVER_TYPE.RSOther
48
+ elif doc.get(HelloCompat.PRIMARY):
49
+ return SERVER_TYPE.RSPrimary
50
+ elif doc.get(HelloCompat.LEGACY_PRIMARY):
51
+ return SERVER_TYPE.RSPrimary
52
+ elif doc.get("secondary"):
53
+ return SERVER_TYPE.RSSecondary
54
+ elif doc.get("arbiterOnly"):
55
+ return SERVER_TYPE.RSArbiter
56
+ else:
57
+ return SERVER_TYPE.RSOther
58
+ elif doc.get("msg") == "isdbgrid":
59
+ return SERVER_TYPE.Mongos
60
+ else:
61
+ return SERVER_TYPE.Standalone
62
+
63
+
64
+ class Hello(Generic[_DocumentType]):
65
+ """Parse a hello response from the server.
66
+
67
+ .. versionadded:: 3.12
68
+ """
69
+
70
+ __slots__ = ("_doc", "_server_type", "_is_writable", "_is_readable", "_awaitable")
71
+
72
+ def __init__(self, doc: _DocumentType, awaitable: bool = False) -> None:
73
+ self._server_type = _get_server_type(doc)
74
+ self._doc: _DocumentType = doc
75
+ self._is_writable = self._server_type in (
76
+ SERVER_TYPE.RSPrimary,
77
+ SERVER_TYPE.Standalone,
78
+ SERVER_TYPE.Mongos,
79
+ SERVER_TYPE.LoadBalancer,
80
+ )
81
+
82
+ self._is_readable = self.server_type == SERVER_TYPE.RSSecondary or self._is_writable
83
+ self._awaitable = awaitable
84
+
85
+ @property
86
+ def document(self) -> _DocumentType:
87
+ """The complete hello command response document.
88
+
89
+ .. versionadded:: 3.4
90
+ """
91
+ return copy.copy(self._doc)
92
+
93
+ @property
94
+ def server_type(self) -> int:
95
+ return self._server_type
96
+
97
+ @property
98
+ def all_hosts(self) -> Set[Tuple[str, int]]:
99
+ """List of hosts, passives, and arbiters known to this server."""
100
+ return set(
101
+ map(
102
+ common.clean_node,
103
+ itertools.chain(
104
+ self._doc.get("hosts", []),
105
+ self._doc.get("passives", []),
106
+ self._doc.get("arbiters", []),
107
+ ),
108
+ )
109
+ )
110
+
111
+ @property
112
+ def tags(self) -> Mapping[str, Any]:
113
+ """Replica set member tags or empty dict."""
114
+ return self._doc.get("tags", {})
115
+
116
+ @property
117
+ def primary(self) -> Optional[Tuple[str, int]]:
118
+ """This server's opinion about who the primary is, or None."""
119
+ if self._doc.get("primary"):
120
+ return common.partition_node(self._doc["primary"])
121
+ else:
122
+ return None
123
+
124
+ @property
125
+ def replica_set_name(self) -> Optional[str]:
126
+ """Replica set name or None."""
127
+ return self._doc.get("setName")
128
+
129
+ @property
130
+ def max_bson_size(self) -> int:
131
+ return self._doc.get("maxBsonObjectSize", common.MAX_BSON_SIZE)
132
+
133
+ @property
134
+ def max_message_size(self) -> int:
135
+ return self._doc.get("maxMessageSizeBytes", 2 * self.max_bson_size)
136
+
137
+ @property
138
+ def max_write_batch_size(self) -> int:
139
+ return self._doc.get("maxWriteBatchSize", common.MAX_WRITE_BATCH_SIZE)
140
+
141
+ @property
142
+ def min_wire_version(self) -> int:
143
+ return self._doc.get("minWireVersion", common.MIN_WIRE_VERSION)
144
+
145
+ @property
146
+ def max_wire_version(self) -> int:
147
+ return self._doc.get("maxWireVersion", common.MAX_WIRE_VERSION)
148
+
149
+ @property
150
+ def set_version(self) -> Optional[int]:
151
+ return self._doc.get("setVersion")
152
+
153
+ @property
154
+ def election_id(self) -> Optional[ObjectId]:
155
+ return self._doc.get("electionId")
156
+
157
+ @property
158
+ def cluster_time(self) -> Optional[Mapping[str, Any]]:
159
+ return self._doc.get("$clusterTime")
160
+
161
+ @property
162
+ def logical_session_timeout_minutes(self) -> Optional[int]:
163
+ return self._doc.get("logicalSessionTimeoutMinutes")
164
+
165
+ @property
166
+ def is_writable(self) -> bool:
167
+ return self._is_writable
168
+
169
+ @property
170
+ def is_readable(self) -> bool:
171
+ return self._is_readable
172
+
173
+ @property
174
+ def me(self) -> Optional[Tuple[str, int]]:
175
+ me = self._doc.get("me")
176
+ if me:
177
+ return common.clean_node(me)
178
+ return None
179
+
180
+ @property
181
+ def last_write_date(self) -> Optional[datetime.datetime]:
182
+ return self._doc.get("lastWrite", {}).get("lastWriteDate")
183
+
184
+ @property
185
+ def compressors(self) -> Optional[List[str]]:
186
+ return self._doc.get("compression")
187
+
188
+ @property
189
+ def sasl_supported_mechs(self) -> List[str]:
190
+ """Supported authentication mechanisms for the current user.
191
+
192
+ For example::
193
+
194
+ >>> hello.sasl_supported_mechs
195
+ ["SCRAM-SHA-1", "SCRAM-SHA-256"]
196
+
197
+ """
198
+ return self._doc.get("saslSupportedMechs", [])
199
+
200
+ @property
201
+ def speculative_authenticate(self) -> Optional[Mapping[str, Any]]:
202
+ """The speculativeAuthenticate field."""
203
+ return self._doc.get("speculativeAuthenticate")
204
+
205
+ @property
206
+ def topology_version(self) -> Optional[Mapping[str, Any]]:
207
+ return self._doc.get("topologyVersion")
208
+
209
+ @property
210
+ def awaitable(self) -> bool:
211
+ return self._awaitable
212
+
213
+ @property
214
+ def service_id(self) -> Optional[ObjectId]:
215
+ return self._doc.get("serviceId")
216
+
217
+ @property
218
+ def hello_ok(self) -> bool:
219
+ return self._doc.get("helloOk", False)
@@ -0,0 +1,259 @@
1
+ # Copyright 2009-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
+ """Bits and pieces used by the driver that don't really fit elsewhere."""
16
+
17
+ import sys
18
+ import traceback
19
+ from collections import abc
20
+ from typing import Any, List, NoReturn
21
+
22
+ from bson.son import SON
23
+ from pymongo import ASCENDING
24
+ from pymongo.errors import (
25
+ CursorNotFound,
26
+ DuplicateKeyError,
27
+ ExecutionTimeout,
28
+ NotPrimaryError,
29
+ OperationFailure,
30
+ WriteConcernError,
31
+ WriteError,
32
+ WTimeoutError,
33
+ _wtimeout_error,
34
+ )
35
+ from pymongo.hello import HelloCompat
36
+
37
+ # From the SDAM spec, the "node is shutting down" codes.
38
+ _SHUTDOWN_CODES = frozenset(
39
+ [
40
+ 11600, # InterruptedAtShutdown
41
+ 91, # ShutdownInProgress
42
+ ]
43
+ )
44
+ # From the SDAM spec, the "not primary" error codes are combined with the
45
+ # "node is recovering" error codes (of which the "node is shutting down"
46
+ # errors are a subset).
47
+ _NOT_PRIMARY_CODES = (
48
+ frozenset(
49
+ [
50
+ 10058, # LegacyNotPrimary <=3.2 "not primary" error code
51
+ 10107, # NotWritablePrimary
52
+ 13435, # NotPrimaryNoSecondaryOk
53
+ 11602, # InterruptedDueToReplStateChange
54
+ 13436, # NotPrimaryOrSecondary
55
+ 189, # PrimarySteppedDown
56
+ ]
57
+ )
58
+ | _SHUTDOWN_CODES
59
+ )
60
+ # From the retryable writes spec.
61
+ _RETRYABLE_ERROR_CODES = _NOT_PRIMARY_CODES | frozenset(
62
+ [
63
+ 7, # HostNotFound
64
+ 6, # HostUnreachable
65
+ 89, # NetworkTimeout
66
+ 9001, # SocketException
67
+ 262, # ExceededTimeLimit
68
+ ]
69
+ )
70
+
71
+
72
+ def _gen_index_name(keys):
73
+ """Generate an index name from the set of fields it is over."""
74
+ return "_".join(["%s_%s" % item for item in keys])
75
+
76
+
77
+ def _index_list(key_or_list, direction=None):
78
+ """Helper to generate a list of (key, direction) pairs.
79
+
80
+ Takes such a list, or a single key, or a single key and direction.
81
+ """
82
+ if direction is not None:
83
+ return [(key_or_list, direction)]
84
+ else:
85
+ if isinstance(key_or_list, str):
86
+ return [(key_or_list, ASCENDING)]
87
+ if isinstance(key_or_list, abc.ItemsView):
88
+ return list(key_or_list)
89
+ elif not isinstance(key_or_list, (list, tuple)):
90
+ raise TypeError("if no direction is specified, key_or_list must be an instance of list")
91
+ return key_or_list
92
+
93
+
94
+ def _index_document(index_list):
95
+ """Helper to generate an index specifying document.
96
+
97
+ Takes a list of (key, direction) pairs.
98
+ """
99
+ if isinstance(index_list, abc.Mapping):
100
+ raise TypeError(
101
+ "passing a dict to sort/create_index/hint is not "
102
+ "allowed - use a list of tuples instead. did you "
103
+ "mean %r?" % list(index_list.items())
104
+ )
105
+ elif not isinstance(index_list, (list, tuple)):
106
+ raise TypeError("must use a list of (key, direction) pairs, not: " + repr(index_list))
107
+ if not len(index_list):
108
+ raise ValueError("key_or_list must not be the empty list")
109
+
110
+ index: SON[str, Any] = SON()
111
+ for (key, value) in index_list:
112
+ if not isinstance(key, str):
113
+ raise TypeError("first item in each key pair must be an instance of str")
114
+ if not isinstance(value, (str, int, abc.Mapping)):
115
+ raise TypeError(
116
+ "second item in each key pair must be 1, -1, "
117
+ "'2d', or another valid MongoDB index specifier."
118
+ )
119
+ index[key] = value
120
+ return index
121
+
122
+
123
+ def _check_command_response(
124
+ response, max_wire_version, allowable_errors=None, parse_write_concern_error=False
125
+ ):
126
+ """Check the response to a command for errors."""
127
+ if "ok" not in response:
128
+ # Server didn't recognize our message as a command.
129
+ raise OperationFailure(
130
+ response.get("$err"), response.get("code"), response, max_wire_version
131
+ )
132
+
133
+ if parse_write_concern_error and "writeConcernError" in response:
134
+ _error = response["writeConcernError"]
135
+ _labels = response.get("errorLabels")
136
+ if _labels:
137
+ _error.update({"errorLabels": _labels})
138
+ _raise_write_concern_error(_error)
139
+
140
+ if response["ok"]:
141
+ return
142
+
143
+ details = response
144
+ # Mongos returns the error details in a 'raw' object
145
+ # for some errors.
146
+ if "raw" in response:
147
+ for shard in response["raw"].values():
148
+ # Grab the first non-empty raw error from a shard.
149
+ if shard.get("errmsg") and not shard.get("ok"):
150
+ details = shard
151
+ break
152
+
153
+ errmsg = details["errmsg"]
154
+ code = details.get("code")
155
+
156
+ # For allowable errors, only check for error messages when the code is not
157
+ # included.
158
+ if allowable_errors:
159
+ if code is not None:
160
+ if code in allowable_errors:
161
+ return
162
+ elif errmsg in allowable_errors:
163
+ return
164
+
165
+ # Server is "not primary" or "recovering"
166
+ if code is not None:
167
+ if code in _NOT_PRIMARY_CODES:
168
+ raise NotPrimaryError(errmsg, response)
169
+ elif HelloCompat.LEGACY_ERROR in errmsg or "node is recovering" in errmsg:
170
+ raise NotPrimaryError(errmsg, response)
171
+
172
+ # Other errors
173
+ # findAndModify with upsert can raise duplicate key error
174
+ if code in (11000, 11001, 12582):
175
+ raise DuplicateKeyError(errmsg, code, response, max_wire_version)
176
+ elif code == 50:
177
+ raise ExecutionTimeout(errmsg, code, response, max_wire_version)
178
+ elif code == 43:
179
+ raise CursorNotFound(errmsg, code, response, max_wire_version)
180
+
181
+ raise OperationFailure(errmsg, code, response, max_wire_version)
182
+
183
+
184
+ def _raise_last_write_error(write_errors: List[Any]) -> NoReturn:
185
+ # If the last batch had multiple errors only report
186
+ # the last error to emulate continue_on_error.
187
+ error = write_errors[-1]
188
+ if error.get("code") == 11000:
189
+ raise DuplicateKeyError(error.get("errmsg"), 11000, error)
190
+ raise WriteError(error.get("errmsg"), error.get("code"), error)
191
+
192
+
193
+ def _raise_write_concern_error(error: Any) -> NoReturn:
194
+ if _wtimeout_error(error):
195
+ # Make sure we raise WTimeoutError
196
+ raise WTimeoutError(error.get("errmsg"), error.get("code"), error)
197
+ raise WriteConcernError(error.get("errmsg"), error.get("code"), error)
198
+
199
+
200
+ def _get_wce_doc(result):
201
+ """Return the writeConcernError or None."""
202
+ wce = result.get("writeConcernError")
203
+ if wce:
204
+ # The server reports errorLabels at the top level but it's more
205
+ # convenient to attach it to the writeConcernError doc itself.
206
+ error_labels = result.get("errorLabels")
207
+ if error_labels:
208
+ wce["errorLabels"] = error_labels
209
+ return wce
210
+
211
+
212
+ def _check_write_command_response(result):
213
+ """Backward compatibility helper for write command error handling."""
214
+ # Prefer write errors over write concern errors
215
+ write_errors = result.get("writeErrors")
216
+ if write_errors:
217
+ _raise_last_write_error(write_errors)
218
+
219
+ wce = _get_wce_doc(result)
220
+ if wce:
221
+ _raise_write_concern_error(wce)
222
+
223
+
224
+ def _fields_list_to_dict(fields, option_name):
225
+ """Takes a sequence of field names and returns a matching dictionary.
226
+
227
+ ["a", "b"] becomes {"a": 1, "b": 1}
228
+
229
+ and
230
+
231
+ ["a.b.c", "d", "a.c"] becomes {"a.b.c": 1, "d": 1, "a.c": 1}
232
+ """
233
+ if isinstance(fields, abc.Mapping):
234
+ return fields
235
+
236
+ if isinstance(fields, (abc.Sequence, abc.Set)):
237
+ if not all(isinstance(field, str) for field in fields):
238
+ raise TypeError(
239
+ "%s must be a list of key names, each an instance of str" % (option_name,)
240
+ )
241
+ return dict.fromkeys(fields, 1)
242
+
243
+ raise TypeError("%s must be a mapping or list of key names" % (option_name,))
244
+
245
+
246
+ def _handle_exception():
247
+ """Print exceptions raised by subscribers to stderr."""
248
+ # Heavily influenced by logging.Handler.handleError.
249
+
250
+ # See note here:
251
+ # https://docs.python.org/3.4/library/sys.html#sys.__stderr__
252
+ if sys.stderr:
253
+ einfo = sys.exc_info()
254
+ try:
255
+ traceback.print_exception(einfo[0], einfo[1], einfo[2], None, sys.stderr)
256
+ except IOError:
257
+ pass
258
+ finally:
259
+ del einfo