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,109 @@
|
|
1
|
+
# Copyright 2014-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
|
+
"""Represent a response from the server."""
|
16
|
+
|
17
|
+
|
18
|
+
class Response(object):
|
19
|
+
__slots__ = ("_data", "_address", "_request_id", "_duration", "_from_command", "_docs")
|
20
|
+
|
21
|
+
def __init__(self, data, address, request_id, duration, from_command, docs):
|
22
|
+
"""Represent a response from the server.
|
23
|
+
|
24
|
+
:Parameters:
|
25
|
+
- `data`: A network response message.
|
26
|
+
- `address`: (host, port) of the source server.
|
27
|
+
- `request_id`: The request id of this operation.
|
28
|
+
- `duration`: The duration of the operation.
|
29
|
+
- `from_command`: if the response is the result of a db command.
|
30
|
+
"""
|
31
|
+
self._data = data
|
32
|
+
self._address = address
|
33
|
+
self._request_id = request_id
|
34
|
+
self._duration = duration
|
35
|
+
self._from_command = from_command
|
36
|
+
self._docs = docs
|
37
|
+
|
38
|
+
@property
|
39
|
+
def data(self):
|
40
|
+
"""Server response's raw BSON bytes."""
|
41
|
+
return self._data
|
42
|
+
|
43
|
+
@property
|
44
|
+
def address(self):
|
45
|
+
"""(host, port) of the source server."""
|
46
|
+
return self._address
|
47
|
+
|
48
|
+
@property
|
49
|
+
def request_id(self):
|
50
|
+
"""The request id of this operation."""
|
51
|
+
return self._request_id
|
52
|
+
|
53
|
+
@property
|
54
|
+
def duration(self):
|
55
|
+
"""The duration of the operation."""
|
56
|
+
return self._duration
|
57
|
+
|
58
|
+
@property
|
59
|
+
def from_command(self):
|
60
|
+
"""If the response is a result from a db command."""
|
61
|
+
return self._from_command
|
62
|
+
|
63
|
+
@property
|
64
|
+
def docs(self):
|
65
|
+
"""The decoded document(s)."""
|
66
|
+
return self._docs
|
67
|
+
|
68
|
+
|
69
|
+
class PinnedResponse(Response):
|
70
|
+
__slots__ = ("_socket_info", "_more_to_come")
|
71
|
+
|
72
|
+
def __init__(
|
73
|
+
self, data, address, socket_info, request_id, duration, from_command, docs, more_to_come
|
74
|
+
):
|
75
|
+
"""Represent a response to an exhaust cursor's initial query.
|
76
|
+
|
77
|
+
:Parameters:
|
78
|
+
- `data`: A network response message.
|
79
|
+
- `address`: (host, port) of the source server.
|
80
|
+
- `socket_info`: The SocketInfo used for the initial query.
|
81
|
+
- `pool`: The Pool from which the SocketInfo came.
|
82
|
+
- `request_id`: The request id of this operation.
|
83
|
+
- `duration`: The duration of the operation.
|
84
|
+
- `from_command`: If the response is the result of a db command.
|
85
|
+
- `docs`: List of documents.
|
86
|
+
- `more_to_come`: Bool indicating whether cursor is ready to be
|
87
|
+
exhausted.
|
88
|
+
"""
|
89
|
+
super(PinnedResponse, self).__init__(
|
90
|
+
data, address, request_id, duration, from_command, docs
|
91
|
+
)
|
92
|
+
self._socket_info = socket_info
|
93
|
+
self._more_to_come = more_to_come
|
94
|
+
|
95
|
+
@property
|
96
|
+
def socket_info(self):
|
97
|
+
"""The SocketInfo used for the initial query.
|
98
|
+
|
99
|
+
The server will send batches on this socket, without waiting for
|
100
|
+
getMores from the client, until the result set is exhausted or there
|
101
|
+
is an error.
|
102
|
+
"""
|
103
|
+
return self._socket_info
|
104
|
+
|
105
|
+
@property
|
106
|
+
def more_to_come(self):
|
107
|
+
"""If true, server is ready to send batches on the socket until the
|
108
|
+
result set is exhausted or there is an error."""
|
109
|
+
return self._more_to_come
|
@@ -0,0 +1,217 @@
|
|
1
|
+
# Copyright 2015-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
|
+
"""Result class definitions."""
|
16
|
+
from typing import Any, Dict, List, Optional, cast
|
17
|
+
|
18
|
+
from pymongo.errors import InvalidOperation
|
19
|
+
|
20
|
+
|
21
|
+
class _WriteResult(object):
|
22
|
+
"""Base class for write result classes."""
|
23
|
+
|
24
|
+
__slots__ = ("__acknowledged",)
|
25
|
+
|
26
|
+
def __init__(self, acknowledged: bool) -> None:
|
27
|
+
self.__acknowledged = acknowledged
|
28
|
+
|
29
|
+
def _raise_if_unacknowledged(self, property_name):
|
30
|
+
"""Raise an exception on property access if unacknowledged."""
|
31
|
+
if not self.__acknowledged:
|
32
|
+
raise InvalidOperation(
|
33
|
+
"A value for %s is not available when "
|
34
|
+
"the write is unacknowledged. Check the "
|
35
|
+
"acknowledged attribute to avoid this "
|
36
|
+
"error." % (property_name,)
|
37
|
+
)
|
38
|
+
|
39
|
+
@property
|
40
|
+
def acknowledged(self) -> bool:
|
41
|
+
"""Is this the result of an acknowledged write operation?
|
42
|
+
|
43
|
+
The :attr:`acknowledged` attribute will be ``False`` when using
|
44
|
+
``WriteConcern(w=0)``, otherwise ``True``.
|
45
|
+
|
46
|
+
.. note::
|
47
|
+
If the :attr:`acknowledged` attribute is ``False`` all other
|
48
|
+
attibutes of this class will raise
|
49
|
+
:class:`~pymongo.errors.InvalidOperation` when accessed. Values for
|
50
|
+
other attributes cannot be determined if the write operation was
|
51
|
+
unacknowledged.
|
52
|
+
|
53
|
+
.. seealso::
|
54
|
+
:class:`~pymongo.write_concern.WriteConcern`
|
55
|
+
"""
|
56
|
+
return self.__acknowledged
|
57
|
+
|
58
|
+
|
59
|
+
class InsertOneResult(_WriteResult):
|
60
|
+
"""The return type for :meth:`~pymongo.collection.Collection.insert_one`."""
|
61
|
+
|
62
|
+
__slots__ = ("__inserted_id",)
|
63
|
+
|
64
|
+
def __init__(self, inserted_id: Any, acknowledged: bool) -> None:
|
65
|
+
self.__inserted_id = inserted_id
|
66
|
+
super(InsertOneResult, self).__init__(acknowledged)
|
67
|
+
|
68
|
+
@property
|
69
|
+
def inserted_id(self) -> Any:
|
70
|
+
"""The inserted document's _id."""
|
71
|
+
return self.__inserted_id
|
72
|
+
|
73
|
+
|
74
|
+
class InsertManyResult(_WriteResult):
|
75
|
+
"""The return type for :meth:`~pymongo.collection.Collection.insert_many`."""
|
76
|
+
|
77
|
+
__slots__ = ("__inserted_ids",)
|
78
|
+
|
79
|
+
def __init__(self, inserted_ids: List[Any], acknowledged: bool) -> None:
|
80
|
+
self.__inserted_ids = inserted_ids
|
81
|
+
super(InsertManyResult, self).__init__(acknowledged)
|
82
|
+
|
83
|
+
@property
|
84
|
+
def inserted_ids(self) -> List:
|
85
|
+
"""A list of _ids of the inserted documents, in the order provided.
|
86
|
+
|
87
|
+
.. note:: If ``False`` is passed for the `ordered` parameter to
|
88
|
+
:meth:`~pymongo.collection.Collection.insert_many` the server
|
89
|
+
may have inserted the documents in a different order than what
|
90
|
+
is presented here.
|
91
|
+
"""
|
92
|
+
return self.__inserted_ids
|
93
|
+
|
94
|
+
|
95
|
+
class UpdateResult(_WriteResult):
|
96
|
+
"""The return type for :meth:`~pymongo.collection.Collection.update_one`,
|
97
|
+
:meth:`~pymongo.collection.Collection.update_many`, and
|
98
|
+
:meth:`~pymongo.collection.Collection.replace_one`.
|
99
|
+
"""
|
100
|
+
|
101
|
+
__slots__ = ("__raw_result",)
|
102
|
+
|
103
|
+
def __init__(self, raw_result: Dict[str, Any], acknowledged: bool) -> None:
|
104
|
+
self.__raw_result = raw_result
|
105
|
+
super(UpdateResult, self).__init__(acknowledged)
|
106
|
+
|
107
|
+
@property
|
108
|
+
def raw_result(self) -> Dict[str, Any]:
|
109
|
+
"""The raw result document returned by the server."""
|
110
|
+
return self.__raw_result
|
111
|
+
|
112
|
+
@property
|
113
|
+
def matched_count(self) -> int:
|
114
|
+
"""The number of documents matched for this update."""
|
115
|
+
self._raise_if_unacknowledged("matched_count")
|
116
|
+
if self.upserted_id is not None:
|
117
|
+
return 0
|
118
|
+
return self.__raw_result.get("n", 0)
|
119
|
+
|
120
|
+
@property
|
121
|
+
def modified_count(self) -> int:
|
122
|
+
"""The number of documents modified."""
|
123
|
+
self._raise_if_unacknowledged("modified_count")
|
124
|
+
return cast(int, self.__raw_result.get("nModified"))
|
125
|
+
|
126
|
+
@property
|
127
|
+
def upserted_id(self) -> Any:
|
128
|
+
"""The _id of the inserted document if an upsert took place. Otherwise
|
129
|
+
``None``.
|
130
|
+
"""
|
131
|
+
self._raise_if_unacknowledged("upserted_id")
|
132
|
+
return self.__raw_result.get("upserted")
|
133
|
+
|
134
|
+
|
135
|
+
class DeleteResult(_WriteResult):
|
136
|
+
"""The return type for :meth:`~pymongo.collection.Collection.delete_one`
|
137
|
+
and :meth:`~pymongo.collection.Collection.delete_many`"""
|
138
|
+
|
139
|
+
__slots__ = ("__raw_result",)
|
140
|
+
|
141
|
+
def __init__(self, raw_result: Dict[str, Any], acknowledged: bool) -> None:
|
142
|
+
self.__raw_result = raw_result
|
143
|
+
super(DeleteResult, self).__init__(acknowledged)
|
144
|
+
|
145
|
+
@property
|
146
|
+
def raw_result(self) -> Dict[str, Any]:
|
147
|
+
"""The raw result document returned by the server."""
|
148
|
+
return self.__raw_result
|
149
|
+
|
150
|
+
@property
|
151
|
+
def deleted_count(self) -> int:
|
152
|
+
"""The number of documents deleted."""
|
153
|
+
self._raise_if_unacknowledged("deleted_count")
|
154
|
+
return self.__raw_result.get("n", 0)
|
155
|
+
|
156
|
+
|
157
|
+
class BulkWriteResult(_WriteResult):
|
158
|
+
"""An object wrapper for bulk API write results."""
|
159
|
+
|
160
|
+
__slots__ = ("__bulk_api_result",)
|
161
|
+
|
162
|
+
def __init__(self, bulk_api_result: Dict[str, Any], acknowledged: bool) -> None:
|
163
|
+
"""Create a BulkWriteResult instance.
|
164
|
+
|
165
|
+
:Parameters:
|
166
|
+
- `bulk_api_result`: A result dict from the bulk API
|
167
|
+
- `acknowledged`: Was this write result acknowledged? If ``False``
|
168
|
+
then all properties of this object will raise
|
169
|
+
:exc:`~pymongo.errors.InvalidOperation`.
|
170
|
+
"""
|
171
|
+
self.__bulk_api_result = bulk_api_result
|
172
|
+
super(BulkWriteResult, self).__init__(acknowledged)
|
173
|
+
|
174
|
+
@property
|
175
|
+
def bulk_api_result(self) -> Dict[str, Any]:
|
176
|
+
"""The raw bulk API result."""
|
177
|
+
return self.__bulk_api_result
|
178
|
+
|
179
|
+
@property
|
180
|
+
def inserted_count(self) -> int:
|
181
|
+
"""The number of documents inserted."""
|
182
|
+
self._raise_if_unacknowledged("inserted_count")
|
183
|
+
return cast(int, self.__bulk_api_result.get("nInserted"))
|
184
|
+
|
185
|
+
@property
|
186
|
+
def matched_count(self) -> int:
|
187
|
+
"""The number of documents matched for an update."""
|
188
|
+
self._raise_if_unacknowledged("matched_count")
|
189
|
+
return cast(int, self.__bulk_api_result.get("nMatched"))
|
190
|
+
|
191
|
+
@property
|
192
|
+
def modified_count(self) -> int:
|
193
|
+
"""The number of documents modified."""
|
194
|
+
self._raise_if_unacknowledged("modified_count")
|
195
|
+
return cast(int, self.__bulk_api_result.get("nModified"))
|
196
|
+
|
197
|
+
@property
|
198
|
+
def deleted_count(self) -> int:
|
199
|
+
"""The number of documents deleted."""
|
200
|
+
self._raise_if_unacknowledged("deleted_count")
|
201
|
+
return cast(int, self.__bulk_api_result.get("nRemoved"))
|
202
|
+
|
203
|
+
@property
|
204
|
+
def upserted_count(self) -> int:
|
205
|
+
"""The number of documents upserted."""
|
206
|
+
self._raise_if_unacknowledged("upserted_count")
|
207
|
+
return cast(int, self.__bulk_api_result.get("nUpserted"))
|
208
|
+
|
209
|
+
@property
|
210
|
+
def upserted_ids(self) -> Optional[Dict[int, Any]]:
|
211
|
+
"""A map of operation index to the _id of the upserted document."""
|
212
|
+
self._raise_if_unacknowledged("upserted_ids")
|
213
|
+
if self.__bulk_api_result:
|
214
|
+
return dict(
|
215
|
+
(upsert["index"], upsert["_id"]) for upsert in self.bulk_api_result["upserted"]
|
216
|
+
)
|
217
|
+
return None
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# Copyright 2016-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
|
+
"""An implementation of RFC4013 SASLprep."""
|
16
|
+
from typing import Any, Optional
|
17
|
+
|
18
|
+
try:
|
19
|
+
import stringprep
|
20
|
+
except ImportError:
|
21
|
+
HAVE_STRINGPREP = False
|
22
|
+
|
23
|
+
def saslprep(data: Any, prohibit_unassigned_code_points: Optional[bool] = True) -> str:
|
24
|
+
"""SASLprep dummy"""
|
25
|
+
if isinstance(data, str):
|
26
|
+
raise TypeError(
|
27
|
+
"The stringprep module is not available. Usernames and "
|
28
|
+
"passwords must be instances of bytes."
|
29
|
+
)
|
30
|
+
return data
|
31
|
+
|
32
|
+
else:
|
33
|
+
HAVE_STRINGPREP = True
|
34
|
+
import unicodedata
|
35
|
+
|
36
|
+
# RFC4013 section 2.3 prohibited output.
|
37
|
+
_PROHIBITED = (
|
38
|
+
# A strict reading of RFC 4013 requires table c12 here, but
|
39
|
+
# characters from it are mapped to SPACE in the Map step. Can
|
40
|
+
# normalization reintroduce them somehow?
|
41
|
+
stringprep.in_table_c12,
|
42
|
+
stringprep.in_table_c21_c22,
|
43
|
+
stringprep.in_table_c3,
|
44
|
+
stringprep.in_table_c4,
|
45
|
+
stringprep.in_table_c5,
|
46
|
+
stringprep.in_table_c6,
|
47
|
+
stringprep.in_table_c7,
|
48
|
+
stringprep.in_table_c8,
|
49
|
+
stringprep.in_table_c9,
|
50
|
+
)
|
51
|
+
|
52
|
+
def saslprep(data: Any, prohibit_unassigned_code_points: Optional[bool] = True) -> str:
|
53
|
+
"""An implementation of RFC4013 SASLprep.
|
54
|
+
|
55
|
+
:Parameters:
|
56
|
+
- `data`: The string to SASLprep. Unicode strings
|
57
|
+
(:class:`str`) are supported. Byte strings
|
58
|
+
(:class:`bytes`) are ignored.
|
59
|
+
- `prohibit_unassigned_code_points`: True / False. RFC 3454
|
60
|
+
and RFCs for various SASL mechanisms distinguish between
|
61
|
+
`queries` (unassigned code points allowed) and
|
62
|
+
`stored strings` (unassigned code points prohibited). Defaults
|
63
|
+
to ``True`` (unassigned code points are prohibited).
|
64
|
+
|
65
|
+
:Returns:
|
66
|
+
The SASLprep'ed version of `data`.
|
67
|
+
"""
|
68
|
+
prohibited: Any
|
69
|
+
|
70
|
+
if not isinstance(data, str):
|
71
|
+
return data
|
72
|
+
|
73
|
+
if prohibit_unassigned_code_points:
|
74
|
+
prohibited = _PROHIBITED + (stringprep.in_table_a1,)
|
75
|
+
else:
|
76
|
+
prohibited = _PROHIBITED
|
77
|
+
|
78
|
+
# RFC3454 section 2, step 1 - Map
|
79
|
+
# RFC4013 section 2.1 mappings
|
80
|
+
# Map Non-ASCII space characters to SPACE (U+0020). Map
|
81
|
+
# commonly mapped to nothing characters to, well, nothing.
|
82
|
+
in_table_c12 = stringprep.in_table_c12
|
83
|
+
in_table_b1 = stringprep.in_table_b1
|
84
|
+
data = "".join(
|
85
|
+
["\u0020" if in_table_c12(elt) else elt for elt in data if not in_table_b1(elt)]
|
86
|
+
)
|
87
|
+
|
88
|
+
# RFC3454 section 2, step 2 - Normalize
|
89
|
+
# RFC4013 section 2.2 normalization
|
90
|
+
data = unicodedata.ucd_3_2_0.normalize("NFKC", data)
|
91
|
+
|
92
|
+
in_table_d1 = stringprep.in_table_d1
|
93
|
+
if in_table_d1(data[0]):
|
94
|
+
if not in_table_d1(data[-1]):
|
95
|
+
# RFC3454, Section 6, #3. If a string contains any
|
96
|
+
# RandALCat character, the first and last characters
|
97
|
+
# MUST be RandALCat characters.
|
98
|
+
raise ValueError("SASLprep: failed bidirectional check")
|
99
|
+
# RFC3454, Section 6, #2. If a string contains any RandALCat
|
100
|
+
# character, it MUST NOT contain any LCat character.
|
101
|
+
prohibited = prohibited + (stringprep.in_table_d2,)
|
102
|
+
else:
|
103
|
+
# RFC3454, Section 6, #3. Following the logic of #3, if
|
104
|
+
# the first character is not a RandALCat, no other character
|
105
|
+
# can be either.
|
106
|
+
prohibited = prohibited + (in_table_d1,)
|
107
|
+
|
108
|
+
# RFC3454 section 2, step 3 and 4 - Prohibit and check bidi
|
109
|
+
for char in data:
|
110
|
+
if any(in_table(char) for in_table in prohibited):
|
111
|
+
raise ValueError("SASLprep: failed prohibited character check")
|
112
|
+
|
113
|
+
return data
|
@@ -0,0 +1,247 @@
|
|
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
|
+
"""Communicate with one MongoDB server in a topology."""
|
16
|
+
|
17
|
+
from datetime import datetime
|
18
|
+
|
19
|
+
from bson import _decode_all_selective
|
20
|
+
from pymongo.errors import NotPrimaryError, OperationFailure
|
21
|
+
from pymongo.helpers import _check_command_response
|
22
|
+
from pymongo.message import _convert_exception, _OpMsg
|
23
|
+
from pymongo.response import PinnedResponse, Response
|
24
|
+
|
25
|
+
_CURSOR_DOC_FIELDS = {"cursor": {"firstBatch": 1, "nextBatch": 1}}
|
26
|
+
|
27
|
+
|
28
|
+
class Server(object):
|
29
|
+
def __init__(
|
30
|
+
self, server_description, pool, monitor, topology_id=None, listeners=None, events=None
|
31
|
+
):
|
32
|
+
"""Represent one MongoDB server."""
|
33
|
+
self._description = server_description
|
34
|
+
self._pool = pool
|
35
|
+
self._monitor = monitor
|
36
|
+
self._topology_id = topology_id
|
37
|
+
self._publish = listeners is not None and listeners.enabled_for_server
|
38
|
+
self._listener = listeners
|
39
|
+
self._events = None
|
40
|
+
if self._publish:
|
41
|
+
self._events = events()
|
42
|
+
|
43
|
+
def open(self):
|
44
|
+
"""Start monitoring, or restart after a fork.
|
45
|
+
|
46
|
+
Multiple calls have no effect.
|
47
|
+
"""
|
48
|
+
if not self._pool.opts.load_balanced:
|
49
|
+
self._monitor.open()
|
50
|
+
|
51
|
+
def reset(self, service_id=None):
|
52
|
+
"""Clear the connection pool."""
|
53
|
+
self.pool.reset(service_id)
|
54
|
+
|
55
|
+
def close(self):
|
56
|
+
"""Clear the connection pool and stop the monitor.
|
57
|
+
|
58
|
+
Reconnect with open().
|
59
|
+
"""
|
60
|
+
if self._publish:
|
61
|
+
assert self._listener is not None
|
62
|
+
assert self._events is not None
|
63
|
+
self._events.put(
|
64
|
+
(
|
65
|
+
self._listener.publish_server_closed,
|
66
|
+
(self._description.address, self._topology_id),
|
67
|
+
)
|
68
|
+
)
|
69
|
+
self._monitor.close()
|
70
|
+
self._pool.reset_without_pause()
|
71
|
+
|
72
|
+
def request_check(self):
|
73
|
+
"""Check the server's state soon."""
|
74
|
+
self._monitor.request_check()
|
75
|
+
|
76
|
+
def run_operation(self, sock_info, operation, read_preference, listeners, unpack_res):
|
77
|
+
"""Run a _Query or _GetMore operation and return a Response object.
|
78
|
+
|
79
|
+
This method is used only to run _Query/_GetMore operations from
|
80
|
+
cursors.
|
81
|
+
Can raise ConnectionFailure, OperationFailure, etc.
|
82
|
+
|
83
|
+
:Parameters:
|
84
|
+
- `sock_info`: A SocketInfo instance.
|
85
|
+
- `operation`: A _Query or _GetMore object.
|
86
|
+
- `set_secondary_okay`: Pass to operation.get_message.
|
87
|
+
- `listeners`: Instance of _EventListeners or None.
|
88
|
+
- `unpack_res`: A callable that decodes the wire protocol response.
|
89
|
+
"""
|
90
|
+
duration = None
|
91
|
+
publish = listeners.enabled_for_commands
|
92
|
+
if publish:
|
93
|
+
start = datetime.now()
|
94
|
+
|
95
|
+
use_cmd = operation.use_command(sock_info)
|
96
|
+
more_to_come = operation.sock_mgr and operation.sock_mgr.more_to_come
|
97
|
+
if more_to_come:
|
98
|
+
request_id = 0
|
99
|
+
else:
|
100
|
+
message = operation.get_message(read_preference, sock_info, use_cmd)
|
101
|
+
request_id, data, max_doc_size = self._split_message(message)
|
102
|
+
|
103
|
+
if publish:
|
104
|
+
cmd, dbn = operation.as_command(sock_info)
|
105
|
+
listeners.publish_command_start(
|
106
|
+
cmd, dbn, request_id, sock_info.address, service_id=sock_info.service_id
|
107
|
+
)
|
108
|
+
start = datetime.now()
|
109
|
+
|
110
|
+
try:
|
111
|
+
if more_to_come:
|
112
|
+
reply = sock_info.receive_message(None)
|
113
|
+
else:
|
114
|
+
sock_info.send_message(data, max_doc_size)
|
115
|
+
reply = sock_info.receive_message(request_id)
|
116
|
+
|
117
|
+
# Unpack and check for command errors.
|
118
|
+
if use_cmd:
|
119
|
+
user_fields = _CURSOR_DOC_FIELDS
|
120
|
+
legacy_response = False
|
121
|
+
else:
|
122
|
+
user_fields = None
|
123
|
+
legacy_response = True
|
124
|
+
docs = unpack_res(
|
125
|
+
reply,
|
126
|
+
operation.cursor_id,
|
127
|
+
operation.codec_options,
|
128
|
+
legacy_response=legacy_response,
|
129
|
+
user_fields=user_fields,
|
130
|
+
)
|
131
|
+
if use_cmd:
|
132
|
+
first = docs[0]
|
133
|
+
operation.client._process_response(first, operation.session)
|
134
|
+
_check_command_response(first, sock_info.max_wire_version)
|
135
|
+
except Exception as exc:
|
136
|
+
if publish:
|
137
|
+
duration = datetime.now() - start
|
138
|
+
if isinstance(exc, (NotPrimaryError, OperationFailure)):
|
139
|
+
failure = exc.details
|
140
|
+
else:
|
141
|
+
failure = _convert_exception(exc)
|
142
|
+
listeners.publish_command_failure(
|
143
|
+
duration,
|
144
|
+
failure,
|
145
|
+
operation.name,
|
146
|
+
request_id,
|
147
|
+
sock_info.address,
|
148
|
+
service_id=sock_info.service_id,
|
149
|
+
)
|
150
|
+
raise
|
151
|
+
|
152
|
+
if publish:
|
153
|
+
duration = datetime.now() - start
|
154
|
+
# Must publish in find / getMore / explain command response
|
155
|
+
# format.
|
156
|
+
if use_cmd:
|
157
|
+
res = docs[0]
|
158
|
+
elif operation.name == "explain":
|
159
|
+
res = docs[0] if docs else {}
|
160
|
+
else:
|
161
|
+
res = {"cursor": {"id": reply.cursor_id, "ns": operation.namespace()}, "ok": 1}
|
162
|
+
if operation.name == "find":
|
163
|
+
res["cursor"]["firstBatch"] = docs
|
164
|
+
else:
|
165
|
+
res["cursor"]["nextBatch"] = docs
|
166
|
+
listeners.publish_command_success(
|
167
|
+
duration,
|
168
|
+
res,
|
169
|
+
operation.name,
|
170
|
+
request_id,
|
171
|
+
sock_info.address,
|
172
|
+
service_id=sock_info.service_id,
|
173
|
+
)
|
174
|
+
|
175
|
+
# Decrypt response.
|
176
|
+
client = operation.client
|
177
|
+
if client and client._encrypter:
|
178
|
+
if use_cmd:
|
179
|
+
decrypted = client._encrypter.decrypt(reply.raw_command_response())
|
180
|
+
docs = _decode_all_selective(decrypted, operation.codec_options, user_fields)
|
181
|
+
|
182
|
+
response: Response
|
183
|
+
|
184
|
+
if client._should_pin_cursor(operation.session) or operation.exhaust:
|
185
|
+
sock_info.pin_cursor()
|
186
|
+
if isinstance(reply, _OpMsg):
|
187
|
+
# In OP_MSG, the server keeps sending only if the
|
188
|
+
# more_to_come flag is set.
|
189
|
+
more_to_come = reply.more_to_come
|
190
|
+
else:
|
191
|
+
# In OP_REPLY, the server keeps sending until cursor_id is 0.
|
192
|
+
more_to_come = bool(operation.exhaust and reply.cursor_id)
|
193
|
+
if operation.sock_mgr:
|
194
|
+
operation.sock_mgr.update_exhaust(more_to_come)
|
195
|
+
response = PinnedResponse(
|
196
|
+
data=reply,
|
197
|
+
address=self._description.address,
|
198
|
+
socket_info=sock_info,
|
199
|
+
duration=duration,
|
200
|
+
request_id=request_id,
|
201
|
+
from_command=use_cmd,
|
202
|
+
docs=docs,
|
203
|
+
more_to_come=more_to_come,
|
204
|
+
)
|
205
|
+
else:
|
206
|
+
response = Response(
|
207
|
+
data=reply,
|
208
|
+
address=self._description.address,
|
209
|
+
duration=duration,
|
210
|
+
request_id=request_id,
|
211
|
+
from_command=use_cmd,
|
212
|
+
docs=docs,
|
213
|
+
)
|
214
|
+
|
215
|
+
return response
|
216
|
+
|
217
|
+
def get_socket(self, handler=None):
|
218
|
+
return self.pool.get_socket(handler)
|
219
|
+
|
220
|
+
@property
|
221
|
+
def description(self):
|
222
|
+
return self._description
|
223
|
+
|
224
|
+
@description.setter
|
225
|
+
def description(self, server_description):
|
226
|
+
assert server_description.address == self._description.address
|
227
|
+
self._description = server_description
|
228
|
+
|
229
|
+
@property
|
230
|
+
def pool(self):
|
231
|
+
return self._pool
|
232
|
+
|
233
|
+
def _split_message(self, message):
|
234
|
+
"""Return request_id, data, max_doc_size.
|
235
|
+
|
236
|
+
:Parameters:
|
237
|
+
- `message`: (request_id, data, max_doc_size) or (request_id, data)
|
238
|
+
"""
|
239
|
+
if len(message) == 3:
|
240
|
+
return message
|
241
|
+
else:
|
242
|
+
# get_more and kill_cursors messages don't include BSON documents.
|
243
|
+
request_id, data = message
|
244
|
+
return request_id, data, 0
|
245
|
+
|
246
|
+
def __repr__(self):
|
247
|
+
return "<%s %r>" % (self.__class__.__name__, self._description)
|