dt-extensions-sdk 1.1.11__py3-none-any.whl → 1.1.12__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.
- {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.12.dist-info}/METADATA +2 -2
- {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.12.dist-info}/RECORD +9 -9
- {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.12.dist-info}/WHEEL +1 -1
- dynatrace_extension/__about__.py +2 -1
- dynatrace_extension/cli/main.py +13 -1
- dynatrace_extension/sdk/communication.py +31 -16
- dynatrace_extension/sdk/extension.py +24 -21
- {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.12.dist-info}/entry_points.txt +0 -0
- {dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.12.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: dt-extensions-sdk
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.12
|
4
4
|
Project-URL: Documentation, https://github.com/dynatrace-extensions/dt-extensions-python-sdk#readme
|
5
5
|
Project-URL: Issues, https://github.com/dynatrace-extensions/dt-extensions-python-sdk/issues
|
6
6
|
Project-URL: Source, https://github.com/dynatrace-extensions/dt-extensions-python-sdk
|
@@ -18,7 +18,7 @@ Requires-Python: >=3.10
|
|
18
18
|
Provides-Extra: cli
|
19
19
|
Requires-Dist: dt-cli>=1.6.13; extra == 'cli'
|
20
20
|
Requires-Dist: pyyaml; extra == 'cli'
|
21
|
-
Requires-Dist: typer
|
21
|
+
Requires-Dist: typer; extra == 'cli'
|
22
22
|
Description-Content-Type: text/markdown
|
23
23
|
|
24
24
|
# Dynatrace Extensions Python SDK
|
@@ -1,7 +1,7 @@
|
|
1
|
-
dynatrace_extension/__about__.py,sha256=
|
1
|
+
dynatrace_extension/__about__.py,sha256=XQBn893nbChfqgenpRBcrSsmllyosJ-0hLOaYr6NUkM,110
|
2
2
|
dynatrace_extension/__init__.py,sha256=BvQuknmA7ti3WJi3zEXZfY7aAxJrie37VNitWICsUvI,752
|
3
3
|
dynatrace_extension/cli/__init__.py,sha256=HCboY_eJPoqjFmoPDsBL8Jk6aNvank8K7JpkVrgwzUM,123
|
4
|
-
dynatrace_extension/cli/main.py,sha256=
|
4
|
+
dynatrace_extension/cli/main.py,sha256=wi3xxji3WbVXViebzLCB5UGNSADUcdYUzGI02EYbsaM,16965
|
5
5
|
dynatrace_extension/cli/schema.py,sha256=d8wKUodRiaU3hfSZDWVNpD15lBfhmif2oQ-k07IxcaA,3230
|
6
6
|
dynatrace_extension/cli/create/__init__.py,sha256=NfyOJCVlxs8dYtfDAMHS1Q5SJTuZcFzOg5rtaI-ZPRE,72
|
7
7
|
dynatrace_extension/cli/create/create.py,sha256=apXden2M93MDDDm7aa-Os-AEtUtyKbk_PsS56j32NK4,2708
|
@@ -16,9 +16,9 @@ dynatrace_extension/cli/create/extension_template/extension_name/__main__.py.tem
|
|
16
16
|
dynatrace_extension/sdk/__init__.py,sha256=RsqQ1heGyCmSK3fhuEKAcxQIRCg4gEK0-eSkIehL5Nc,86
|
17
17
|
dynatrace_extension/sdk/activation.py,sha256=goTbT1tD2kn8xfyXFdTy_cTZNcFPJpgbvQM8HOzKECA,1480
|
18
18
|
dynatrace_extension/sdk/callback.py,sha256=x1ImFqAhomfqWtoOkQLksWqNlIi_3E3v3TaWhNndauU,6165
|
19
|
-
dynatrace_extension/sdk/communication.py,sha256=
|
19
|
+
dynatrace_extension/sdk/communication.py,sha256=7N516-6g2IKzmWi_4PFPU_mkz49SG8KTPi7MrRxgTF8,17255
|
20
20
|
dynatrace_extension/sdk/event.py,sha256=J261imbFKpxfuAQ6Nfu3RRcsIQKKivy6fme1nww2g-8,388
|
21
|
-
dynatrace_extension/sdk/extension.py,sha256=
|
21
|
+
dynatrace_extension/sdk/extension.py,sha256=B79fvNB4KoQ8x5MnEvb9EO4a5xeNBzk3EF1I_8RZ2Nc,40213
|
22
22
|
dynatrace_extension/sdk/helper.py,sha256=ZNrO9ao2hE3KQ934vAYD74k0fCr6QTG-_bAvbk9-hi8,6562
|
23
23
|
dynatrace_extension/sdk/metric.py,sha256=7VClzJCFJNDCxA-d69uTu1pdPtDZBTwq7fbafs_L6nQ,3690
|
24
24
|
dynatrace_extension/sdk/runtime.py,sha256=jyYsM1x-gMnW68eWq8IoZZZBarHgIcr_nVeGDDgpRDk,2802
|
@@ -26,8 +26,8 @@ dynatrace_extension/sdk/vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
|
|
26
26
|
dynatrace_extension/sdk/vendor/mureq/LICENSE,sha256=8AVcgZgiT_mvK1fOofXtRRr2f1dRXS_K21NuxQgP4VM,671
|
27
27
|
dynatrace_extension/sdk/vendor/mureq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
28
|
dynatrace_extension/sdk/vendor/mureq/mureq.py,sha256=TQ3xcpfwLEYIU1TU65A9OqWqwIKqyO8SSUFeuCvE60Y,14655
|
29
|
-
dt_extensions_sdk-1.1.
|
30
|
-
dt_extensions_sdk-1.1.
|
31
|
-
dt_extensions_sdk-1.1.
|
32
|
-
dt_extensions_sdk-1.1.
|
33
|
-
dt_extensions_sdk-1.1.
|
29
|
+
dt_extensions_sdk-1.1.12.dist-info/METADATA,sha256=DK_2-CG9dk8x2V-x2lUUQkOjh2OOg-eH1EW16bNTY8w,2789
|
30
|
+
dt_extensions_sdk-1.1.12.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
31
|
+
dt_extensions_sdk-1.1.12.dist-info/entry_points.txt,sha256=pweyOCgENGHjOlT6_kXYaBPOrE3p18K0UettqnNlnoE,55
|
32
|
+
dt_extensions_sdk-1.1.12.dist-info/licenses/LICENSE.txt,sha256=3Zihv0lOVYHNfDkJC-tUAU6euP9r2NexsDW4w-zqgVk,1078
|
33
|
+
dt_extensions_sdk-1.1.12.dist-info/RECORD,,
|
dynatrace_extension/__about__.py
CHANGED
dynatrace_extension/cli/main.py
CHANGED
@@ -38,6 +38,7 @@ def run(
|
|
38
38
|
fast_check: bool = typer.Option(False, "--fastcheck"),
|
39
39
|
local_ingest: bool = typer.Option(False, "--local-ingest"),
|
40
40
|
local_ingest_port: int = typer.Option(14499, "--local-ingest-port"),
|
41
|
+
print_metrics: bool = typer.Option(True),
|
41
42
|
):
|
42
43
|
"""
|
43
44
|
Runs an extension, this is used during development to locally run and test an extension
|
@@ -47,6 +48,7 @@ def run(
|
|
47
48
|
:param fast_check: If true, run a fastcheck and exits
|
48
49
|
:param local_ingest: If true, send metrics to localhost:14499 on top of printing them
|
49
50
|
:param local_ingest_port: The port to send metrics to, by default this is 14499
|
51
|
+
:param print_metrics: If true, print metrics to the console
|
50
52
|
"""
|
51
53
|
|
52
54
|
# This parses the yaml, which validates it before running
|
@@ -58,6 +60,8 @@ def run(
|
|
58
60
|
if local_ingest:
|
59
61
|
command.append("--local-ingest")
|
60
62
|
command.append(f"--local-ingest-port={local_ingest_port}")
|
63
|
+
if not print_metrics:
|
64
|
+
command.append("--no-print-metrics")
|
61
65
|
run_process(command, cwd=extension_dir)
|
62
66
|
except KeyboardInterrupt:
|
63
67
|
console.print("\nRun interrupted with a KeyboardInterrupt, stopping", style="bold yellow")
|
@@ -79,6 +83,7 @@ def build(
|
|
79
83
|
extra_index_url: Optional[str] = typer.Option(
|
80
84
|
None, "--extra-index-url", "-i", help="Extra index url to use when downloading dependencies"
|
81
85
|
),
|
86
|
+
find_links: Optional[str] = typer.Option( None, "--find-links", "-f", help="Extra index url to use when downloading dependencies" ),
|
82
87
|
):
|
83
88
|
"""
|
84
89
|
Builds and signs an extension using the developer fused key-certificate
|
@@ -90,6 +95,7 @@ def build(
|
|
90
95
|
folder
|
91
96
|
:param extra_platforms: Attempt to also download wheels for an extra platform (e.g. manylinux1_x86_64 or win_amd64)
|
92
97
|
:param extra_index_url: Extra index url to use when downloading dependencies
|
98
|
+
:param find_links: Extra index url to use when downloading dependencies
|
93
99
|
"""
|
94
100
|
console.print(f"Building and signing extension from {extension_dir} to {target_directory}", style="cyan")
|
95
101
|
if target_directory is None:
|
@@ -98,7 +104,7 @@ def build(
|
|
98
104
|
target_directory.mkdir()
|
99
105
|
|
100
106
|
console.print("Stage 1 - Download and build dependencies", style="bold blue")
|
101
|
-
wheel(extension_dir, extra_platforms, extra_index_url)
|
107
|
+
wheel(extension_dir, extra_platforms, extra_index_url, find_links)
|
102
108
|
|
103
109
|
console.print("Stage 2 - Create the extension zip file", style="bold blue")
|
104
110
|
built_zip = assemble(extension_dir, target_directory)
|
@@ -163,6 +169,7 @@ def wheel(
|
|
163
169
|
extra_index_url: Optional[str] = typer.Option(
|
164
170
|
None, "--extra-index-url", "-i", help="Extra index url to use when downloading dependencies"
|
165
171
|
),
|
172
|
+
find_links: Optional[str] = typer.Option( None, "--find-links", "-f", help="Extra index url to use when downloading dependencies" ),
|
166
173
|
):
|
167
174
|
"""
|
168
175
|
Builds the extension and it's dependencies into wheel files
|
@@ -171,6 +178,7 @@ def wheel(
|
|
171
178
|
:param extension_dir: The directory of the extension, by default this is the current directory
|
172
179
|
:param extra_platforms: Attempt to also download wheels for an extra platform (e.g. manylinux1_x86_64 or win_amd64)
|
173
180
|
:param extra_index_url: Extra index url to use when downloading dependencies
|
181
|
+
:param find_links: Extra index url to use when downloading dependencies
|
174
182
|
"""
|
175
183
|
relative_lib_folder_dir = "extension/lib"
|
176
184
|
lib_folder: Path = extension_dir / relative_lib_folder_dir
|
@@ -182,6 +190,8 @@ def wheel(
|
|
182
190
|
command = [sys.executable, "-m", "pip", "wheel", "-w", relative_lib_folder_dir]
|
183
191
|
if extra_index_url is not None:
|
184
192
|
command.extend(["--extra-index-url", extra_index_url])
|
193
|
+
if find_links is not None:
|
194
|
+
command.extend(["--find-links", find_links])
|
185
195
|
command.append(".")
|
186
196
|
run_process(command, cwd=extension_dir)
|
187
197
|
|
@@ -201,6 +211,8 @@ def wheel(
|
|
201
211
|
]
|
202
212
|
if extra_index_url:
|
203
213
|
command.extend(["--extra-index-url", extra_index_url])
|
214
|
+
if find_links:
|
215
|
+
command.extend(["--find-links", find_links])
|
204
216
|
command.append(".")
|
205
217
|
|
206
218
|
run_process(command, cwd=extension_dir)
|
@@ -6,7 +6,9 @@ from __future__ import annotations
|
|
6
6
|
|
7
7
|
import json
|
8
8
|
import logging
|
9
|
+
import random
|
9
10
|
import sys
|
11
|
+
import time
|
10
12
|
from abc import ABC, abstractmethod
|
11
13
|
from dataclasses import dataclass
|
12
14
|
from enum import Enum
|
@@ -114,8 +116,6 @@ class HttpClient(CommunicationClient):
|
|
114
116
|
"""
|
115
117
|
|
116
118
|
def __init__(self, base_url: str, datasource_id: str, id_token_file_path: str, logger: logging.Logger):
|
117
|
-
# TODO - Do we need to replace 127.0.0.1 with localhost?
|
118
|
-
|
119
119
|
self._activation_config_url = f"{base_url}/userconfig/{datasource_id}"
|
120
120
|
self._extension_config_url = f"{base_url}/extconfig/{datasource_id}"
|
121
121
|
self._metric_url = f"{base_url}/mint/{datasource_id}"
|
@@ -324,6 +324,7 @@ class DebugClient(CommunicationClient):
|
|
324
324
|
logger: logging.Logger,
|
325
325
|
local_ingest: bool = False,
|
326
326
|
local_ingest_port: int = 14499,
|
327
|
+
print_metrics: bool = True
|
327
328
|
):
|
328
329
|
self.activation_config = {}
|
329
330
|
if activation_config_path and Path(activation_config_path).exists():
|
@@ -339,6 +340,7 @@ class DebugClient(CommunicationClient):
|
|
339
340
|
self.logger = logger
|
340
341
|
self.local_ingest = local_ingest
|
341
342
|
self.local_ingest_port = local_ingest_port
|
343
|
+
self.print_metrics = print_metrics
|
342
344
|
|
343
345
|
def get_activation_config(self) -> dict:
|
344
346
|
return self.activation_config
|
@@ -386,23 +388,36 @@ class DebugClient(CommunicationClient):
|
|
386
388
|
return self.send_status(Status())
|
387
389
|
|
388
390
|
def send_metrics(self, mint_lines: list[str]) -> list[MintResponse]:
|
391
|
+
total_lines = len(mint_lines)
|
392
|
+
lines_sent = 0
|
393
|
+
|
394
|
+
self.logger.info(f"Start sending {total_lines} metrics to the EEC")
|
395
|
+
|
389
396
|
responses = []
|
390
|
-
for line in mint_lines:
|
391
|
-
self.logger.info(f"send_metric: {line}")
|
392
397
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
398
|
+
chunks = divide_into_chunks(mint_lines, MAX_MINT_LINES_PER_REQUEST)
|
399
|
+
for chunk in chunks:
|
400
|
+
lines_in_chunk = len(chunk)
|
401
|
+
lines_sent += lines_in_chunk
|
402
|
+
self.logger.debug(f"Sending chunk with {lines_in_chunk} metric lines. ({lines_sent}/{total_lines})")
|
403
|
+
|
404
|
+
if self.local_ingest:
|
405
|
+
mint_data = "\n".join(chunk).encode("utf-8")
|
406
|
+
response = request(
|
407
|
+
"POST",
|
408
|
+
f"http://localhost:{self.local_ingest_port}/metrics/ingest",
|
409
|
+
body=mint_data,
|
410
|
+
headers={"Content-Type": CONTENT_TYPE_PLAIN},
|
411
|
+
).json()
|
412
|
+
mint_response = MintResponse.from_json(response)
|
413
|
+
responses.append(mint_response)
|
414
|
+
else:
|
415
|
+
if self.print_metrics:
|
416
|
+
for line in mint_lines:
|
417
|
+
self.logger.info(f"send_metric: {line}")
|
403
418
|
|
404
|
-
|
405
|
-
|
419
|
+
response = MintResponse(lines_invalid=0, lines_ok=len(chunk), error=None, warnings=None)
|
420
|
+
responses.append(response)
|
406
421
|
return responses
|
407
422
|
|
408
423
|
def send_events(self, events: dict | list[dict], eec_enrichment: bool = True) -> dict | None:
|
@@ -159,8 +159,6 @@ class Extension:
|
|
159
159
|
if hasattr(self, "logger"):
|
160
160
|
return
|
161
161
|
|
162
|
-
# TODO - Move the logging implementation to its own file
|
163
|
-
# TODO - Add sfm logging
|
164
162
|
self.logger = extension_logger
|
165
163
|
|
166
164
|
self.extension_config: str = ""
|
@@ -313,7 +311,6 @@ class Extension:
|
|
313
311
|
api_logger.debug(f"Scheduling callback {callback}")
|
314
312
|
|
315
313
|
# These properties are updated after the extension starts
|
316
|
-
# TODO - These should be part of an ext singleton object instead
|
317
314
|
callback.cluster_time_diff = self._cluster_time_diff
|
318
315
|
callback.running_in_sim = self._running_in_sim
|
319
316
|
self._scheduled_callbacks.append(callback)
|
@@ -698,14 +695,21 @@ class Extension:
|
|
698
695
|
# Debug parameters, these are used when running the extension locally
|
699
696
|
parser.add_argument("--extensionconfig", required=False, default=None)
|
700
697
|
parser.add_argument("--activationconfig", required=False, default="activation.json")
|
698
|
+
parser.add_argument("--no-print-metrics", required=False, action="store_true")
|
701
699
|
|
702
700
|
args, unknown = parser.parse_known_args()
|
703
701
|
self._is_fastcheck = args.fastcheck
|
704
702
|
if args.dsid is None:
|
705
703
|
# DEV mode
|
706
704
|
self._running_in_sim = True
|
705
|
+
print_metrics = not args.no_print_metrics
|
707
706
|
self._client = DebugClient(
|
708
|
-
args.activationconfig,
|
707
|
+
activation_config_path=args.activationconfig,
|
708
|
+
extension_config_path=args.extensionconfig,
|
709
|
+
logger=api_logger,
|
710
|
+
local_ingest=args.local_ingest,
|
711
|
+
local_ingest_port=args.local_ingest_port,
|
712
|
+
print_metrics=print_metrics
|
709
713
|
)
|
710
714
|
RuntimeProperties.set_default_log_level(args.loglevel)
|
711
715
|
else:
|
@@ -810,23 +814,22 @@ class Extension:
|
|
810
814
|
self._scheduler.enter(SFM_METRIC_SENDING_INTERVAL.total_seconds(), 1, self._sfm_metrics_iteration)
|
811
815
|
|
812
816
|
def _send_metrics(self):
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
self._metrics = []
|
817
|
+
with self._metrics_lock:
|
818
|
+
with self._internal_callbacks_results_lock:
|
819
|
+
if self._metrics:
|
820
|
+
number_of_metrics = len(self._metrics)
|
821
|
+
responses = self._client.send_metrics(self._metrics)
|
822
|
+
|
823
|
+
self._internal_callbacks_results[self._send_metrics.__name__] = Status(StatusValue.OK)
|
824
|
+
lines_invalid = sum(response.lines_invalid for response in responses)
|
825
|
+
if lines_invalid > 0:
|
826
|
+
message = f"{lines_invalid} invalid metric lines found"
|
827
|
+
self._internal_callbacks_results[self._send_metrics.__name__] = Status(
|
828
|
+
StatusValue.GENERIC_ERROR, message
|
829
|
+
)
|
830
|
+
|
831
|
+
api_logger.info(f"Sent {number_of_metrics} metric lines to EEC: {responses}")
|
832
|
+
self._metrics = []
|
830
833
|
|
831
834
|
def _prepare_sfm_metrics(self) -> List[str]:
|
832
835
|
"""Prepare self monitoring metrics.
|
File without changes
|
{dt_extensions_sdk-1.1.11.dist-info → dt_extensions_sdk-1.1.12.dist-info}/licenses/LICENSE.txt
RENAMED
File without changes
|