cavisson-pythonagent 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cavisson_pythonagent-0.0.1.dist-info/METADATA +32 -0
- cavisson_pythonagent-0.0.1.dist-info/RECORD +180 -0
- cavisson_pythonagent-0.0.1.dist-info/WHEEL +5 -0
- cavisson_pythonagent-0.0.1.dist-info/licenses/LICENSE +19 -0
- cavisson_pythonagent-0.0.1.dist-info/top_level.txt +1 -0
- pythonagent/__init__.py +22 -0
- pythonagent/agent/__init__.py +219 -0
- pythonagent/agent/internal/__init__.py +1 -0
- pythonagent/agent/internal/agent.py +1651 -0
- pythonagent/agent/internal/framesinfo.py +152 -0
- pythonagent/agent/internal/heap_dump.py +39 -0
- pythonagent/agent/internal/intercept_module.py +70 -0
- pythonagent/agent/internal/logs.py +122 -0
- pythonagent/agent/internal/metadata/__init__.py +0 -0
- pythonagent/agent/internal/metadata/agent_meta_data.py +276 -0
- pythonagent/agent/internal/proc_compat.py +75 -0
- pythonagent/agent/internal/profile.py +47 -0
- pythonagent/agent/internal/provider.py +83 -0
- pythonagent/agent/internal/thread_dump.py +85 -0
- pythonagent/agent/internal/udp.py +245 -0
- pythonagent/agent/internal/udp_message.py +800 -0
- pythonagent/agent/probes/Instrumentation/__init__.py +340 -0
- pythonagent/agent/probes/Instrumentation/find.py +243 -0
- pythonagent/agent/probes/Instrumentation/module_version_resolver.py +155 -0
- pythonagent/agent/probes/Instrumentation/new_parser.py +64 -0
- pythonagent/agent/probes/Instrumentation/parser.py +129 -0
- pythonagent/agent/probes/__init__.py +219 -0
- pythonagent/agent/probes/base.py +303 -0
- pythonagent/agent/probes/cache/__init__.py +51 -0
- pythonagent/agent/probes/cache/redis.py +119 -0
- pythonagent/agent/probes/cache/redis_asyncio.py +83 -0
- pythonagent/agent/probes/coroutines/__init__.py +1 -0
- pythonagent/agent/probes/coroutines/asyncio.py +63 -0
- pythonagent/agent/probes/elasticdb/__init__.py +7 -0
- pythonagent/agent/probes/elasticdb/aelastic.py +54 -0
- pythonagent/agent/probes/frameworks/__init__.py +27 -0
- pythonagent/agent/probes/frameworks/agentprofiler.py +105 -0
- pythonagent/agent/probes/frameworks/aiohttp_web.py +155 -0
- pythonagent/agent/probes/frameworks/aisess.py +151 -0
- pythonagent/agent/probes/frameworks/asgi.py +340 -0
- pythonagent/agent/probes/frameworks/bottle.py +27 -0
- pythonagent/agent/probes/frameworks/cherry.py +25 -0
- pythonagent/agent/probes/frameworks/django.py +128 -0
- pythonagent/agent/probes/frameworks/falcon.py +21 -0
- pythonagent/agent/probes/frameworks/fastapi.py +35 -0
- pythonagent/agent/probes/frameworks/flask.py +30 -0
- pythonagent/agent/probes/frameworks/pyramid.py +56 -0
- pythonagent/agent/probes/frameworks/test.py +108 -0
- pythonagent/agent/probes/frameworks/tornado_async_web.py +117 -0
- pythonagent/agent/probes/frameworks/tornado_web.py +133 -0
- pythonagent/agent/probes/frameworks/wsgi.py +353 -0
- pythonagent/agent/probes/grpc/__init__.py +76 -0
- pythonagent/agent/probes/grpc/client_interceptor.py +132 -0
- pythonagent/agent/probes/grpc/server_interceptor.py +129 -0
- pythonagent/agent/probes/havoc/__init__.py +0 -0
- pythonagent/agent/probes/havoc/custom_memory_stress.py +186 -0
- pythonagent/agent/probes/havoc/custom_thread_stress.py +187 -0
- pythonagent/agent/probes/havoc/havoc_constants.py +218 -0
- pythonagent/agent/probes/havoc/havoc_manager.py +981 -0
- pythonagent/agent/probes/http/__init__.py +49 -0
- pythonagent/agent/probes/http/aiohttp_client.py +59 -0
- pythonagent/agent/probes/http/boto.py +12 -0
- pythonagent/agent/probes/http/httplib.py +110 -0
- pythonagent/agent/probes/http/httpx_client.py +116 -0
- pythonagent/agent/probes/http/requests.py +15 -0
- pythonagent/agent/probes/http/tornado_httpclient.py +85 -0
- pythonagent/agent/probes/http/urllib3.py +16 -0
- pythonagent/agent/probes/langchain/__init__.py +21 -0
- pythonagent/agent/probes/langchain/base_tool.py +95 -0
- pythonagent/agent/probes/langchain/langchain_community.py +136 -0
- pythonagent/agent/probes/langchain/langchain_core.py +32 -0
- pythonagent/agent/probes/langchain/langchain_openai.py +110 -0
- pythonagent/agent/probes/logging/__init__.py +106 -0
- pythonagent/agent/probes/message_brokers/__init__.py +4 -0
- pythonagent/agent/probes/message_brokers/pika.py +126 -0
- pythonagent/agent/probes/mongodb/__init__.py +6 -0
- pythonagent/agent/probes/mongodb/pymongo.py +286 -0
- pythonagent/agent/probes/openai/__init__.py +3 -0
- pythonagent/agent/probes/openai/openai.py +797 -0
- pythonagent/agent/probes/span.py +101 -0
- pythonagent/agent/probes/sql/__init__.py +13 -0
- pythonagent/agent/probes/sql/botocores3.py +51 -0
- pythonagent/agent/probes/sql/dbapi.py +285 -0
- pythonagent/agent/probes/sql/dynamodb.py +90 -0
- pythonagent/agent/probes/sql/mysql_connector.py +24 -0
- pythonagent/agent/probes/sql/mysql_connector_cext.py +24 -0
- pythonagent/agent/probes/sql/mysqldb.py +43 -0
- pythonagent/agent/probes/sql/psycopg2.py +174 -0
- pythonagent/agent/probes/sql/pymysql.py +25 -0
- pythonagent/bootstrap/__init__.py +0 -0
- pythonagent/bootstrap/cav_gunicorn.py +26 -0
- pythonagent/bootstrap/cavagent_lambda_wrapper.py +291 -0
- pythonagent/bootstrap/run.py +47 -0
- pythonagent/bootstrap/sitecustomize.py +287 -0
- pythonagent/cavisson/netdiagnostics/CavAgent/instrumentationprofile.json +26 -0
- pythonagent/cavisson/netdiagnostics/CavAgent/interceptor_points.txt +29 -0
- pythonagent/cavisson/netdiagnostics/python/CavAgent/instrumentationprofile.json +42 -0
- pythonagent/cavisson/netdiagnostics/python/CavAgent/interceptor_points.txt +29 -0
- pythonagent/cavisson/netdiagnostics/python/config/ndsettings.conf +6 -0
- pythonagent/config.py +279 -0
- pythonagent/find.py +72 -0
- pythonagent/find_mod_cls_name.py +54 -0
- pythonagent/lang.py +131 -0
- pythonagent/lib.py +91 -0
- pythonagent/main/__init__.py +0 -0
- pythonagent/main/pytrace/__init__.py +79 -0
- pythonagent/main/pytrace/commands/__init__.py +0 -0
- pythonagent/main/pytrace/commands/auto_discovery.py +25 -0
- pythonagent/main/pytrace/commands/run.py +401 -0
- pythonagent/main/pytrace/pytrace.py +133 -0
- pythonagent/main/wsgi.py +6 -0
- pythonagent/main.py +27 -0
- pythonagent/run.py +46 -0
- pythonagent/sqins.py +8 -0
- pythonagent/test.py +73 -0
- pythonagent/utils.py +168 -0
- pythonagent/vendor/__init__.py +0 -0
- pythonagent/vendor/pympler/__init__.py +1 -0
- pythonagent/vendor/pympler/asizeof.py +2810 -0
- pythonagent/vendor/pympler/charts.py +62 -0
- pythonagent/vendor/pympler/classtracker.py +590 -0
- pythonagent/vendor/pympler/classtracker_stats.py +780 -0
- pythonagent/vendor/pympler/garbagegraph.py +80 -0
- pythonagent/vendor/pympler/mprofile.py +97 -0
- pythonagent/vendor/pympler/muppy.py +275 -0
- pythonagent/vendor/pympler/panels.py +115 -0
- pythonagent/vendor/pympler/process.py +238 -0
- pythonagent/vendor/pympler/py.typed +0 -0
- pythonagent/vendor/pympler/refbrowser.py +451 -0
- pythonagent/vendor/pympler/refgraph.py +350 -0
- pythonagent/vendor/pympler/summary.py +321 -0
- pythonagent/vendor/pympler/tracker.py +267 -0
- pythonagent/vendor/pympler/util/__init__.py +0 -0
- pythonagent/vendor/pympler/util/bottle.py +3809 -0
- pythonagent/vendor/pympler/util/compat.py +23 -0
- pythonagent/vendor/pympler/util/stringutils.py +77 -0
- pythonagent/vendor/pympler/web.py +346 -0
- pythonagent/vendor/werkzeug/__init__.py +20 -0
- pythonagent/vendor/werkzeug/_compat.py +228 -0
- pythonagent/vendor/werkzeug/_internal.py +473 -0
- pythonagent/vendor/werkzeug/_reloader.py +341 -0
- pythonagent/vendor/werkzeug/datastructures.py +3120 -0
- pythonagent/vendor/werkzeug/debug/__init__.py +498 -0
- pythonagent/vendor/werkzeug/debug/console.py +218 -0
- pythonagent/vendor/werkzeug/debug/repr.py +297 -0
- pythonagent/vendor/werkzeug/debug/tbtools.py +628 -0
- pythonagent/vendor/werkzeug/exceptions.py +829 -0
- pythonagent/vendor/werkzeug/filesystem.py +64 -0
- pythonagent/vendor/werkzeug/formparser.py +584 -0
- pythonagent/vendor/werkzeug/http.py +1307 -0
- pythonagent/vendor/werkzeug/local.py +420 -0
- pythonagent/vendor/werkzeug/middleware/__init__.py +25 -0
- pythonagent/vendor/werkzeug/middleware/dispatcher.py +66 -0
- pythonagent/vendor/werkzeug/middleware/http_proxy.py +219 -0
- pythonagent/vendor/werkzeug/middleware/lint.py +408 -0
- pythonagent/vendor/werkzeug/middleware/profiler.py +132 -0
- pythonagent/vendor/werkzeug/middleware/proxy_fix.py +169 -0
- pythonagent/vendor/werkzeug/middleware/shared_data.py +293 -0
- pythonagent/vendor/werkzeug/posixemulation.py +117 -0
- pythonagent/vendor/werkzeug/routing.py +2210 -0
- pythonagent/vendor/werkzeug/security.py +249 -0
- pythonagent/vendor/werkzeug/serving.py +1117 -0
- pythonagent/vendor/werkzeug/test.py +1123 -0
- pythonagent/vendor/werkzeug/testapp.py +241 -0
- pythonagent/vendor/werkzeug/urls.py +1138 -0
- pythonagent/vendor/werkzeug/useragents.py +202 -0
- pythonagent/vendor/werkzeug/utils.py +778 -0
- pythonagent/vendor/werkzeug/wrappers/__init__.py +36 -0
- pythonagent/vendor/werkzeug/wrappers/accept.py +50 -0
- pythonagent/vendor/werkzeug/wrappers/auth.py +33 -0
- pythonagent/vendor/werkzeug/wrappers/base_request.py +673 -0
- pythonagent/vendor/werkzeug/wrappers/base_response.py +700 -0
- pythonagent/vendor/werkzeug/wrappers/common_descriptors.py +341 -0
- pythonagent/vendor/werkzeug/wrappers/cors.py +100 -0
- pythonagent/vendor/werkzeug/wrappers/etag.py +304 -0
- pythonagent/vendor/werkzeug/wrappers/json.py +145 -0
- pythonagent/vendor/werkzeug/wrappers/request.py +49 -0
- pythonagent/vendor/werkzeug/wrappers/response.py +84 -0
- pythonagent/vendor/werkzeug/wrappers/user_agent.py +14 -0
- pythonagent/vendor/werkzeug/wsgi.py +1000 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
from __future__ import unicode_literals
|
|
2
|
+
#import sys
|
|
3
|
+
import inspect
|
|
4
|
+
|
|
5
|
+
from ..base import ExitCallInterceptor
|
|
6
|
+
#import abc
|
|
7
|
+
#import agent
|
|
8
|
+
#from agent.internal.proxy import *
|
|
9
|
+
from pythonagent.lang import str
|
|
10
|
+
from pythonagent.utils import generate_flow_path_id, get_utf8_bytes, get_current_timestamp_in_us, get_validated_duration
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
EXIT_CUSTOM = 1
|
|
14
|
+
EXIT_SUBTYPE_MONGODB = 'Mongo DB'
|
|
15
|
+
funcName = 'pymongo.monitoring.CommandListener.started'
|
|
16
|
+
|
|
17
|
+
COLLECTION_METHODS_FOR_HAVOC = [
|
|
18
|
+
'find_one', 'find_one_and_update', 'find_one_and_replace', 'find_one_and_delete',
|
|
19
|
+
'insert_one', 'insert_many',
|
|
20
|
+
'update_one', 'update_many', 'replace_one',
|
|
21
|
+
'delete_one', 'delete_many',
|
|
22
|
+
'aggregate', 'count_documents', 'bulk_write',
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_collection_host_info(col):
|
|
27
|
+
"""Extract (host, port, db_name) from a Collection object. Never raises.
|
|
28
|
+
|
|
29
|
+
Sync-only helper for pymongo.MongoClient/Collection, where `.address` and
|
|
30
|
+
`.nodes` are plain properties. Do NOT use this for AsyncCollection — use
|
|
31
|
+
_get_collection_host_info_async instead, since AsyncMongoClient.address
|
|
32
|
+
is a coroutine and awaiting it here isn't possible from a sync function.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
client = col.database.client
|
|
36
|
+
address = getattr(client, 'address', None)
|
|
37
|
+
if not address:
|
|
38
|
+
nodes = getattr(client, 'nodes', None)
|
|
39
|
+
address = next(iter(nodes), None) if nodes else None
|
|
40
|
+
if address:
|
|
41
|
+
host, port = address
|
|
42
|
+
else:
|
|
43
|
+
host = getattr(client, 'HOST', 'localhost')
|
|
44
|
+
port = getattr(client, 'PORT', 27017)
|
|
45
|
+
db_name = col.database.name
|
|
46
|
+
return host, port, db_name
|
|
47
|
+
except Exception:
|
|
48
|
+
return 'unknown', 27017, 'unknown'
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def _get_collection_host_info_async(col):
|
|
52
|
+
"""Async-safe (host, port, db_name) extraction for pymongo.asynchronous.AsyncCollection.
|
|
53
|
+
|
|
54
|
+
On the native async driver, AsyncMongoClient.address (and .nodes) are
|
|
55
|
+
coroutines and must be awaited. Calling the sync _get_collection_host_info
|
|
56
|
+
against an AsyncCollection creates-but-never-awaits that coroutine, which
|
|
57
|
+
silently falls into the except-fallback AND leaks an unawaited coroutine
|
|
58
|
+
object — that's the source of:
|
|
59
|
+
|
|
60
|
+
RuntimeWarning: coroutine 'AsyncMongoClient.address' was never awaited
|
|
61
|
+
|
|
62
|
+
This variant detects the awaitable and awaits it properly instead.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
client = col.database.client
|
|
66
|
+
address = getattr(client, 'address', None)
|
|
67
|
+
if inspect.isawaitable(address):
|
|
68
|
+
address = await address
|
|
69
|
+
if not address:
|
|
70
|
+
nodes = getattr(client, 'nodes', None)
|
|
71
|
+
if inspect.isawaitable(nodes):
|
|
72
|
+
nodes = await nodes
|
|
73
|
+
address = next(iter(nodes), None) if nodes else None
|
|
74
|
+
if address:
|
|
75
|
+
host, port = address
|
|
76
|
+
else:
|
|
77
|
+
host = getattr(client, 'HOST', 'localhost')
|
|
78
|
+
port = getattr(client, 'PORT', 27017)
|
|
79
|
+
db_name = col.database.name
|
|
80
|
+
return host, port, db_name
|
|
81
|
+
except Exception:
|
|
82
|
+
return 'unknown', 27017, 'unknown'
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _patch_collection_for_havoc(agent, collection_cls):
|
|
86
|
+
"""Monkey-patch Collection CRUD methods so NDHavocException propagates to the caller.
|
|
87
|
+
|
|
88
|
+
The CommandListener monitoring API cannot do this — PyMongo swallows all exceptions
|
|
89
|
+
raised inside monitoring callbacks, so NDHavocException never reaches the application.
|
|
90
|
+
Wrapping Collection methods directly ensures the exception is in the call stack.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def make_wrapper(original_method, mname):
|
|
94
|
+
def havoc_wrapper(col_self, *args, **kwargs):
|
|
95
|
+
bt = agent.get_current_bt()
|
|
96
|
+
if bt:
|
|
97
|
+
host, port, db_name = _get_collection_host_info(col_self)
|
|
98
|
+
host_str = "NA|{}|{}|NA|MONGODB|{}|NA|NA|NA|NA|NA".format(host, port, db_name)
|
|
99
|
+
try:
|
|
100
|
+
from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
|
|
101
|
+
backend_name_value = agent.setting_backend_name_otel(host_str, "DB")
|
|
102
|
+
if backend_name_value:
|
|
103
|
+
backend_name = (
|
|
104
|
+
backend_name_value.decode()
|
|
105
|
+
if isinstance(backend_name_value, bytes)
|
|
106
|
+
else backend_name_value
|
|
107
|
+
)
|
|
108
|
+
else:
|
|
109
|
+
backend_name = None
|
|
110
|
+
ctx = agent.get_transaction_context()
|
|
111
|
+
btname = ctx.btname if ctx else None
|
|
112
|
+
req_url = ctx.url_path if ctx else None
|
|
113
|
+
agent.havoc_monitor.apply_outbound_service_failure(host_str, backend_name,req_url,btname)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
|
|
116
|
+
if isinstance(e, NDHavocException):
|
|
117
|
+
agent.logger.info("MongoDB Collection havoc triggered for {}: {}".format(mname, e))
|
|
118
|
+
agent.set_current_status_code(503)
|
|
119
|
+
db_start_time = get_current_timestamp_in_us()
|
|
120
|
+
ip_handle = agent.db_call_begin(funcName, bt, str(host), mname, None, db_start_time)
|
|
121
|
+
agent.db_call_end(bt, ip_handle, 0, db_start_time, funcName, host_str, 503)
|
|
122
|
+
raise e
|
|
123
|
+
agent.logger.debug("Non-havoc exception in MongoDB havoc check: {}".format(e))
|
|
124
|
+
return original_method(col_self, *args, **kwargs)
|
|
125
|
+
|
|
126
|
+
havoc_wrapper._pythonagent_intercepted = True
|
|
127
|
+
return havoc_wrapper
|
|
128
|
+
|
|
129
|
+
for method_name in COLLECTION_METHODS_FOR_HAVOC:
|
|
130
|
+
original = getattr(collection_cls, method_name, None)
|
|
131
|
+
if original is None:
|
|
132
|
+
continue
|
|
133
|
+
if getattr(original, '_pythonagent_intercepted', False):
|
|
134
|
+
continue
|
|
135
|
+
setattr(collection_cls, method_name, make_wrapper(original, method_name))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _patch_async_collection_for_havoc(agent, async_collection_cls):
|
|
139
|
+
"""Async mirror of _patch_collection_for_havoc for pymongo.asynchronous.AsyncCollection.
|
|
140
|
+
|
|
141
|
+
AsyncCollection methods are coroutines (async def). The wrapper must also be
|
|
142
|
+
async def and must await the original method so the call chain is preserved.
|
|
143
|
+
Havoc checks are synchronous and safe to call from async context; time.sleep
|
|
144
|
+
in apply_outbound_service_delay intentionally blocks the event loop for the delay.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
def make_async_wrapper(original_method, mname):
|
|
148
|
+
async def havoc_wrapper(col_self, *args, **kwargs):
|
|
149
|
+
bt = agent.get_current_bt()
|
|
150
|
+
if bt:
|
|
151
|
+
host, port, db_name = await _get_collection_host_info_async(col_self)
|
|
152
|
+
host_str = "NA|{}|{}|NA|MONGODB|{}|NA|NA|NA|NA|NA".format(host, port, db_name)
|
|
153
|
+
try:
|
|
154
|
+
from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
|
|
155
|
+
backend_name_value = agent.setting_backend_name_otel(host_str, "DB")
|
|
156
|
+
if backend_name_value:
|
|
157
|
+
backend_name = (
|
|
158
|
+
backend_name_value.decode()
|
|
159
|
+
if isinstance(backend_name_value, bytes)
|
|
160
|
+
else backend_name_value
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
backend_name = None
|
|
164
|
+
ctx = agent.get_transaction_context()
|
|
165
|
+
btname = ctx.btname if ctx else None
|
|
166
|
+
req_url = ctx.url_path if ctx else None
|
|
167
|
+
agent.havoc_monitor.apply_outbound_service_failure(host_str, backend_name,req_url,btname)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
|
|
170
|
+
if isinstance(e, NDHavocException):
|
|
171
|
+
agent.logger.info("MongoDB AsyncCollection havoc triggered for {}: {}".format(mname, e))
|
|
172
|
+
agent.set_current_status_code(503)
|
|
173
|
+
db_start_time = get_current_timestamp_in_us()
|
|
174
|
+
ip_handle = agent.db_call_begin(funcName, bt, str(host), mname, None, db_start_time)
|
|
175
|
+
agent.db_call_end(bt, ip_handle, 0, db_start_time, funcName, host_str, 503)
|
|
176
|
+
raise e
|
|
177
|
+
agent.logger.debug("Non-havoc exception in MongoDB async havoc check: {}".format(e))
|
|
178
|
+
return await original_method(col_self, *args, **kwargs)
|
|
179
|
+
|
|
180
|
+
havoc_wrapper._pythonagent_intercepted = True
|
|
181
|
+
return havoc_wrapper
|
|
182
|
+
|
|
183
|
+
for method_name in COLLECTION_METHODS_FOR_HAVOC:
|
|
184
|
+
original = getattr(async_collection_cls, method_name, None)
|
|
185
|
+
if original is None:
|
|
186
|
+
continue
|
|
187
|
+
if getattr(original, '_pythonagent_intercepted', False):
|
|
188
|
+
continue
|
|
189
|
+
setattr(async_collection_cls, method_name, make_async_wrapper(original, method_name))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def intercept_pymongo(agent, mod):
|
|
193
|
+
class ExitCallListener(mod.monitoring.CommandListener):
|
|
194
|
+
backend_name_format_string = '{HOST}:{PORT} - {DATABASE}'
|
|
195
|
+
|
|
196
|
+
def __init__(self):
|
|
197
|
+
self.interceptor = ExitCallInterceptor(agent, None)
|
|
198
|
+
self.entry_point_fqm = None
|
|
199
|
+
self.exit_call_map = {}
|
|
200
|
+
|
|
201
|
+
def get_backend(self, connection_id, database_name):
|
|
202
|
+
host, port = connection_id
|
|
203
|
+
backend_properties = {
|
|
204
|
+
'HOST': host,
|
|
205
|
+
'PORT': str(port),
|
|
206
|
+
'DATABASE': database_name,
|
|
207
|
+
}
|
|
208
|
+
agent.logger.info("Modulenameintercept_pymongo class || backend_properties is :{0}".format(backend_properties))
|
|
209
|
+
return backend_properties
|
|
210
|
+
|
|
211
|
+
def started(self, event):
|
|
212
|
+
try:
|
|
213
|
+
db_start_time = 0
|
|
214
|
+
host_, port_ = event.connection_id
|
|
215
|
+
# format :- "PROTOCOL|HOST|PORT|URL|DBPRODNAME|DBNAME|DBPRODVER|DRIVERNAME|DRIVERVER|USERNAME|SERVICENAME"
|
|
216
|
+
host_name_str = f"NA|{host_}|{port_}|NA|MONGODB|{event.database_name}|NA|NA|NA|NA|NA"
|
|
217
|
+
if not self.entry_point_fqm:
|
|
218
|
+
mName = mod.monitoring.CommandListener.__module__ + "." + mod.monitoring.CommandListener.__name__ + ".started"
|
|
219
|
+
self.entry_point_fqm = mName
|
|
220
|
+
|
|
221
|
+
bt = self.interceptor.bt
|
|
222
|
+
if not bt:
|
|
223
|
+
bt = agent.get_current_bt()
|
|
224
|
+
if bt:
|
|
225
|
+
backend = self.get_backend(event.connection_id, event.database_name)
|
|
226
|
+
if backend:
|
|
227
|
+
if backend:
|
|
228
|
+
db_start_time = get_current_timestamp_in_us()
|
|
229
|
+
agent.logger.debug("db_call_begin for mongodb bt {} host {} query {} query_params {} db_start_time {}".format(bt, backend["HOST"], str(event.command_name), None, db_start_time))
|
|
230
|
+
exit_call = self.interceptor.db_call_begin(bt, backend["HOST"], str(event.command_name), None, db_start_time, self.entry_point_fqm, host_name_str)
|
|
231
|
+
self.exit_call_map[event.operation_id] = (exit_call, host_name_str, db_start_time)
|
|
232
|
+
except Exception as e:
|
|
233
|
+
from pythonagent.agent.probes.havoc.havoc_manager import NDHavocException
|
|
234
|
+
if isinstance(e, NDHavocException):
|
|
235
|
+
raise e
|
|
236
|
+
agent.logger.exception("Exception occurred in pymongo started {}".format(e))
|
|
237
|
+
|
|
238
|
+
def succeeded(self, event):
|
|
239
|
+
try:
|
|
240
|
+
if not self.interceptor.bt:
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
db_end_time = get_current_timestamp_in_us()
|
|
244
|
+
ip_handle, host_str, db_start_time = self.exit_call_map.pop(event.operation_id, None)
|
|
245
|
+
duration = get_validated_duration(db_start_time, db_end_time, "MongoDB")
|
|
246
|
+
status_code = 200
|
|
247
|
+
|
|
248
|
+
agent.logger.debug("db_call_end for mongodb succeeded bt {} ip_handle {} duration {} db_start_time {}".format(self.interceptor.bt, ip_handle, duration, db_start_time))
|
|
249
|
+
self.interceptor.db_call_end(self.interceptor.bt, ip_handle, duration, db_start_time, self.entry_point_fqm, host_str, status_code)
|
|
250
|
+
except Exception as e:
|
|
251
|
+
self.agent.logger.exception("Exception occurred in pymongo succeeded {}".format(e))
|
|
252
|
+
|
|
253
|
+
def failed(self, event):
|
|
254
|
+
try:
|
|
255
|
+
if not self.interceptor.bt:
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
db_end_time = get_current_timestamp_in_us()
|
|
259
|
+
ip_handle, host_str, db_start_time = self.exit_call_map.pop(event.operation_id, None)
|
|
260
|
+
duration = get_validated_duration(db_start_time, db_end_time, "MongoDB")
|
|
261
|
+
status_code = 500
|
|
262
|
+
agent.logger.debug("db_call_end for mongodb failed bt {} ip_handle {} duration {} db_start_time {}".format(self.interceptor.bt, ip_handle, duration, db_start_time))
|
|
263
|
+
self.interceptor.db_call_end(self.interceptor.bt, ip_handle, duration, db_start_time, self.entry_point_fqm, host_str, status_code)
|
|
264
|
+
except Exception as e:
|
|
265
|
+
self.agent.logger.exception("Exception occurred in pymongo failed {}".format(e))
|
|
266
|
+
|
|
267
|
+
mod.monitoring.register(ExitCallListener())
|
|
268
|
+
|
|
269
|
+
# Patch Collection CRUD methods so NDHavocException propagates to the caller.
|
|
270
|
+
# CommandListener cannot do this — PyMongo swallows exceptions from monitoring callbacks.
|
|
271
|
+
try:
|
|
272
|
+
import importlib
|
|
273
|
+
_patch_collection_for_havoc(agent, importlib.import_module('pymongo.collection').Collection)
|
|
274
|
+
except Exception as e:
|
|
275
|
+
agent.logger.warning("Could not patch pymongo Collection for havoc injection: {}".format(e))
|
|
276
|
+
|
|
277
|
+
# Patch AsyncCollection for PyMongo 4.7+ native async API.
|
|
278
|
+
# Gracefully skipped on older versions where pymongo.asynchronous does not exist.
|
|
279
|
+
try:
|
|
280
|
+
import importlib
|
|
281
|
+
_patch_async_collection_for_havoc(
|
|
282
|
+
agent,
|
|
283
|
+
importlib.import_module('pymongo.asynchronous.collection').AsyncCollection
|
|
284
|
+
)
|
|
285
|
+
except Exception as e:
|
|
286
|
+
agent.logger.warning("Could not patch pymongo AsyncCollection for havoc injection: {}".format(e))
|