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.
Files changed (180) hide show
  1. cavisson_pythonagent-0.0.1.dist-info/METADATA +32 -0
  2. cavisson_pythonagent-0.0.1.dist-info/RECORD +180 -0
  3. cavisson_pythonagent-0.0.1.dist-info/WHEEL +5 -0
  4. cavisson_pythonagent-0.0.1.dist-info/licenses/LICENSE +19 -0
  5. cavisson_pythonagent-0.0.1.dist-info/top_level.txt +1 -0
  6. pythonagent/__init__.py +22 -0
  7. pythonagent/agent/__init__.py +219 -0
  8. pythonagent/agent/internal/__init__.py +1 -0
  9. pythonagent/agent/internal/agent.py +1651 -0
  10. pythonagent/agent/internal/framesinfo.py +152 -0
  11. pythonagent/agent/internal/heap_dump.py +39 -0
  12. pythonagent/agent/internal/intercept_module.py +70 -0
  13. pythonagent/agent/internal/logs.py +122 -0
  14. pythonagent/agent/internal/metadata/__init__.py +0 -0
  15. pythonagent/agent/internal/metadata/agent_meta_data.py +276 -0
  16. pythonagent/agent/internal/proc_compat.py +75 -0
  17. pythonagent/agent/internal/profile.py +47 -0
  18. pythonagent/agent/internal/provider.py +83 -0
  19. pythonagent/agent/internal/thread_dump.py +85 -0
  20. pythonagent/agent/internal/udp.py +245 -0
  21. pythonagent/agent/internal/udp_message.py +800 -0
  22. pythonagent/agent/probes/Instrumentation/__init__.py +340 -0
  23. pythonagent/agent/probes/Instrumentation/find.py +243 -0
  24. pythonagent/agent/probes/Instrumentation/module_version_resolver.py +155 -0
  25. pythonagent/agent/probes/Instrumentation/new_parser.py +64 -0
  26. pythonagent/agent/probes/Instrumentation/parser.py +129 -0
  27. pythonagent/agent/probes/__init__.py +219 -0
  28. pythonagent/agent/probes/base.py +303 -0
  29. pythonagent/agent/probes/cache/__init__.py +51 -0
  30. pythonagent/agent/probes/cache/redis.py +119 -0
  31. pythonagent/agent/probes/cache/redis_asyncio.py +83 -0
  32. pythonagent/agent/probes/coroutines/__init__.py +1 -0
  33. pythonagent/agent/probes/coroutines/asyncio.py +63 -0
  34. pythonagent/agent/probes/elasticdb/__init__.py +7 -0
  35. pythonagent/agent/probes/elasticdb/aelastic.py +54 -0
  36. pythonagent/agent/probes/frameworks/__init__.py +27 -0
  37. pythonagent/agent/probes/frameworks/agentprofiler.py +105 -0
  38. pythonagent/agent/probes/frameworks/aiohttp_web.py +155 -0
  39. pythonagent/agent/probes/frameworks/aisess.py +151 -0
  40. pythonagent/agent/probes/frameworks/asgi.py +340 -0
  41. pythonagent/agent/probes/frameworks/bottle.py +27 -0
  42. pythonagent/agent/probes/frameworks/cherry.py +25 -0
  43. pythonagent/agent/probes/frameworks/django.py +128 -0
  44. pythonagent/agent/probes/frameworks/falcon.py +21 -0
  45. pythonagent/agent/probes/frameworks/fastapi.py +35 -0
  46. pythonagent/agent/probes/frameworks/flask.py +30 -0
  47. pythonagent/agent/probes/frameworks/pyramid.py +56 -0
  48. pythonagent/agent/probes/frameworks/test.py +108 -0
  49. pythonagent/agent/probes/frameworks/tornado_async_web.py +117 -0
  50. pythonagent/agent/probes/frameworks/tornado_web.py +133 -0
  51. pythonagent/agent/probes/frameworks/wsgi.py +353 -0
  52. pythonagent/agent/probes/grpc/__init__.py +76 -0
  53. pythonagent/agent/probes/grpc/client_interceptor.py +132 -0
  54. pythonagent/agent/probes/grpc/server_interceptor.py +129 -0
  55. pythonagent/agent/probes/havoc/__init__.py +0 -0
  56. pythonagent/agent/probes/havoc/custom_memory_stress.py +186 -0
  57. pythonagent/agent/probes/havoc/custom_thread_stress.py +187 -0
  58. pythonagent/agent/probes/havoc/havoc_constants.py +218 -0
  59. pythonagent/agent/probes/havoc/havoc_manager.py +981 -0
  60. pythonagent/agent/probes/http/__init__.py +49 -0
  61. pythonagent/agent/probes/http/aiohttp_client.py +59 -0
  62. pythonagent/agent/probes/http/boto.py +12 -0
  63. pythonagent/agent/probes/http/httplib.py +110 -0
  64. pythonagent/agent/probes/http/httpx_client.py +116 -0
  65. pythonagent/agent/probes/http/requests.py +15 -0
  66. pythonagent/agent/probes/http/tornado_httpclient.py +85 -0
  67. pythonagent/agent/probes/http/urllib3.py +16 -0
  68. pythonagent/agent/probes/langchain/__init__.py +21 -0
  69. pythonagent/agent/probes/langchain/base_tool.py +95 -0
  70. pythonagent/agent/probes/langchain/langchain_community.py +136 -0
  71. pythonagent/agent/probes/langchain/langchain_core.py +32 -0
  72. pythonagent/agent/probes/langchain/langchain_openai.py +110 -0
  73. pythonagent/agent/probes/logging/__init__.py +106 -0
  74. pythonagent/agent/probes/message_brokers/__init__.py +4 -0
  75. pythonagent/agent/probes/message_brokers/pika.py +126 -0
  76. pythonagent/agent/probes/mongodb/__init__.py +6 -0
  77. pythonagent/agent/probes/mongodb/pymongo.py +286 -0
  78. pythonagent/agent/probes/openai/__init__.py +3 -0
  79. pythonagent/agent/probes/openai/openai.py +797 -0
  80. pythonagent/agent/probes/span.py +101 -0
  81. pythonagent/agent/probes/sql/__init__.py +13 -0
  82. pythonagent/agent/probes/sql/botocores3.py +51 -0
  83. pythonagent/agent/probes/sql/dbapi.py +285 -0
  84. pythonagent/agent/probes/sql/dynamodb.py +90 -0
  85. pythonagent/agent/probes/sql/mysql_connector.py +24 -0
  86. pythonagent/agent/probes/sql/mysql_connector_cext.py +24 -0
  87. pythonagent/agent/probes/sql/mysqldb.py +43 -0
  88. pythonagent/agent/probes/sql/psycopg2.py +174 -0
  89. pythonagent/agent/probes/sql/pymysql.py +25 -0
  90. pythonagent/bootstrap/__init__.py +0 -0
  91. pythonagent/bootstrap/cav_gunicorn.py +26 -0
  92. pythonagent/bootstrap/cavagent_lambda_wrapper.py +291 -0
  93. pythonagent/bootstrap/run.py +47 -0
  94. pythonagent/bootstrap/sitecustomize.py +287 -0
  95. pythonagent/cavisson/netdiagnostics/CavAgent/instrumentationprofile.json +26 -0
  96. pythonagent/cavisson/netdiagnostics/CavAgent/interceptor_points.txt +29 -0
  97. pythonagent/cavisson/netdiagnostics/python/CavAgent/instrumentationprofile.json +42 -0
  98. pythonagent/cavisson/netdiagnostics/python/CavAgent/interceptor_points.txt +29 -0
  99. pythonagent/cavisson/netdiagnostics/python/config/ndsettings.conf +6 -0
  100. pythonagent/config.py +279 -0
  101. pythonagent/find.py +72 -0
  102. pythonagent/find_mod_cls_name.py +54 -0
  103. pythonagent/lang.py +131 -0
  104. pythonagent/lib.py +91 -0
  105. pythonagent/main/__init__.py +0 -0
  106. pythonagent/main/pytrace/__init__.py +79 -0
  107. pythonagent/main/pytrace/commands/__init__.py +0 -0
  108. pythonagent/main/pytrace/commands/auto_discovery.py +25 -0
  109. pythonagent/main/pytrace/commands/run.py +401 -0
  110. pythonagent/main/pytrace/pytrace.py +133 -0
  111. pythonagent/main/wsgi.py +6 -0
  112. pythonagent/main.py +27 -0
  113. pythonagent/run.py +46 -0
  114. pythonagent/sqins.py +8 -0
  115. pythonagent/test.py +73 -0
  116. pythonagent/utils.py +168 -0
  117. pythonagent/vendor/__init__.py +0 -0
  118. pythonagent/vendor/pympler/__init__.py +1 -0
  119. pythonagent/vendor/pympler/asizeof.py +2810 -0
  120. pythonagent/vendor/pympler/charts.py +62 -0
  121. pythonagent/vendor/pympler/classtracker.py +590 -0
  122. pythonagent/vendor/pympler/classtracker_stats.py +780 -0
  123. pythonagent/vendor/pympler/garbagegraph.py +80 -0
  124. pythonagent/vendor/pympler/mprofile.py +97 -0
  125. pythonagent/vendor/pympler/muppy.py +275 -0
  126. pythonagent/vendor/pympler/panels.py +115 -0
  127. pythonagent/vendor/pympler/process.py +238 -0
  128. pythonagent/vendor/pympler/py.typed +0 -0
  129. pythonagent/vendor/pympler/refbrowser.py +451 -0
  130. pythonagent/vendor/pympler/refgraph.py +350 -0
  131. pythonagent/vendor/pympler/summary.py +321 -0
  132. pythonagent/vendor/pympler/tracker.py +267 -0
  133. pythonagent/vendor/pympler/util/__init__.py +0 -0
  134. pythonagent/vendor/pympler/util/bottle.py +3809 -0
  135. pythonagent/vendor/pympler/util/compat.py +23 -0
  136. pythonagent/vendor/pympler/util/stringutils.py +77 -0
  137. pythonagent/vendor/pympler/web.py +346 -0
  138. pythonagent/vendor/werkzeug/__init__.py +20 -0
  139. pythonagent/vendor/werkzeug/_compat.py +228 -0
  140. pythonagent/vendor/werkzeug/_internal.py +473 -0
  141. pythonagent/vendor/werkzeug/_reloader.py +341 -0
  142. pythonagent/vendor/werkzeug/datastructures.py +3120 -0
  143. pythonagent/vendor/werkzeug/debug/__init__.py +498 -0
  144. pythonagent/vendor/werkzeug/debug/console.py +218 -0
  145. pythonagent/vendor/werkzeug/debug/repr.py +297 -0
  146. pythonagent/vendor/werkzeug/debug/tbtools.py +628 -0
  147. pythonagent/vendor/werkzeug/exceptions.py +829 -0
  148. pythonagent/vendor/werkzeug/filesystem.py +64 -0
  149. pythonagent/vendor/werkzeug/formparser.py +584 -0
  150. pythonagent/vendor/werkzeug/http.py +1307 -0
  151. pythonagent/vendor/werkzeug/local.py +420 -0
  152. pythonagent/vendor/werkzeug/middleware/__init__.py +25 -0
  153. pythonagent/vendor/werkzeug/middleware/dispatcher.py +66 -0
  154. pythonagent/vendor/werkzeug/middleware/http_proxy.py +219 -0
  155. pythonagent/vendor/werkzeug/middleware/lint.py +408 -0
  156. pythonagent/vendor/werkzeug/middleware/profiler.py +132 -0
  157. pythonagent/vendor/werkzeug/middleware/proxy_fix.py +169 -0
  158. pythonagent/vendor/werkzeug/middleware/shared_data.py +293 -0
  159. pythonagent/vendor/werkzeug/posixemulation.py +117 -0
  160. pythonagent/vendor/werkzeug/routing.py +2210 -0
  161. pythonagent/vendor/werkzeug/security.py +249 -0
  162. pythonagent/vendor/werkzeug/serving.py +1117 -0
  163. pythonagent/vendor/werkzeug/test.py +1123 -0
  164. pythonagent/vendor/werkzeug/testapp.py +241 -0
  165. pythonagent/vendor/werkzeug/urls.py +1138 -0
  166. pythonagent/vendor/werkzeug/useragents.py +202 -0
  167. pythonagent/vendor/werkzeug/utils.py +778 -0
  168. pythonagent/vendor/werkzeug/wrappers/__init__.py +36 -0
  169. pythonagent/vendor/werkzeug/wrappers/accept.py +50 -0
  170. pythonagent/vendor/werkzeug/wrappers/auth.py +33 -0
  171. pythonagent/vendor/werkzeug/wrappers/base_request.py +673 -0
  172. pythonagent/vendor/werkzeug/wrappers/base_response.py +700 -0
  173. pythonagent/vendor/werkzeug/wrappers/common_descriptors.py +341 -0
  174. pythonagent/vendor/werkzeug/wrappers/cors.py +100 -0
  175. pythonagent/vendor/werkzeug/wrappers/etag.py +304 -0
  176. pythonagent/vendor/werkzeug/wrappers/json.py +145 -0
  177. pythonagent/vendor/werkzeug/wrappers/request.py +49 -0
  178. pythonagent/vendor/werkzeug/wrappers/response.py +84 -0
  179. pythonagent/vendor/werkzeug/wrappers/user_agent.py +14 -0
  180. 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))
@@ -0,0 +1,3 @@
1
+ from .openai import intercept_openai
2
+
3
+ __all__ = ['intercept_openai']