datadog-checks-base 37.12.0__py2.py3-none-any.whl → 37.15.0__py2.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.
- datadog_checks/base/__about__.py +1 -1
- datadog_checks/base/checks/_config_ast.py +84 -0
- datadog_checks/base/checks/base.py +2 -6
- datadog_checks/base/utils/db/utils.py +127 -1
- {datadog_checks_base-37.12.0.dist-info → datadog_checks_base-37.15.0.dist-info}/METADATA +10 -10
- {datadog_checks_base-37.12.0.dist-info → datadog_checks_base-37.15.0.dist-info}/RECORD +7 -6
- {datadog_checks_base-37.12.0.dist-info → datadog_checks_base-37.15.0.dist-info}/WHEEL +0 -0
datadog_checks/base/__about__.py
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
# This module is used to parse and modify the config AST before it is loaded.
|
|
6
|
+
# It is used to handle special float values (inf, -inf, nan) and replace them with placeholders since those
|
|
7
|
+
# are not valid Python literals.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SpecialFloatPlaceholder(str, Enum):
|
|
11
|
+
INF = '__PYTHON_INF__'
|
|
12
|
+
NEG_INF = '__PYTHON_NEG_INF__'
|
|
13
|
+
NAN = '__PYTHON_NAN__'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _SpecialFloatValuesTransformer(ast.NodeTransformer):
|
|
17
|
+
def visit_Name(self, node: ast.Name) -> ast.AST:
|
|
18
|
+
"""
|
|
19
|
+
Processes named constants like 'inf' and 'nan'.
|
|
20
|
+
If the name is 'inf', it's replaced with a placeholder for positive infinity.
|
|
21
|
+
If the name is 'nan', it's replaced with a placeholder for Not a Number.
|
|
22
|
+
Other names are returned unchanged.
|
|
23
|
+
"""
|
|
24
|
+
if node.id == 'inf':
|
|
25
|
+
return ast.Constant(value=SpecialFloatPlaceholder.INF.value)
|
|
26
|
+
elif node.id == 'nan':
|
|
27
|
+
return ast.Constant(value=SpecialFloatPlaceholder.NAN.value)
|
|
28
|
+
return node # Leaf node, no children to visit
|
|
29
|
+
|
|
30
|
+
def visit_UnaryOp(self, node: ast.UnaryOp) -> ast.AST:
|
|
31
|
+
"""
|
|
32
|
+
Processes unary operations like negation.
|
|
33
|
+
If the operation is a negation ('-') applied to the name 'inf',
|
|
34
|
+
it's replaced with a placeholder for negative infinity.
|
|
35
|
+
We can't use visit_Name for this because the constant is 'inf' and not '-inf'.
|
|
36
|
+
Other unary operations are processed as normal.
|
|
37
|
+
"""
|
|
38
|
+
if isinstance(node.op, ast.USub) and isinstance(node.operand, ast.Name) and node.operand.id == 'inf':
|
|
39
|
+
return ast.Constant(value=SpecialFloatPlaceholder.NEG_INF.value)
|
|
40
|
+
return self.generic_visit(node)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _restore_special_floats(data: Any) -> Any:
|
|
44
|
+
"""
|
|
45
|
+
Restores placeholders for special float values (inf, -inf, nan) to their actual
|
|
46
|
+
float values in a nested data structure.
|
|
47
|
+
"""
|
|
48
|
+
if isinstance(data, dict):
|
|
49
|
+
return {key: _restore_special_floats(value) for key, value in data.items()}
|
|
50
|
+
elif isinstance(data, list):
|
|
51
|
+
return [_restore_special_floats(item) for item in data]
|
|
52
|
+
elif isinstance(data, str):
|
|
53
|
+
if data == SpecialFloatPlaceholder.INF.value:
|
|
54
|
+
return float('inf')
|
|
55
|
+
elif data == SpecialFloatPlaceholder.NEG_INF.value:
|
|
56
|
+
return float('-inf')
|
|
57
|
+
elif data == SpecialFloatPlaceholder.NAN.value:
|
|
58
|
+
return float('nan')
|
|
59
|
+
return data
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def parse(object_string: str) -> Any:
|
|
63
|
+
"""
|
|
64
|
+
Parses a printed Python object, handling special float values (inf, -inf, nan).
|
|
65
|
+
If any error occurs, the original string is returned.
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
if not object_string:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
# Parse the string as a Python expression
|
|
72
|
+
ast_node = ast.parse(object_string, mode='eval').body
|
|
73
|
+
|
|
74
|
+
# Replace inf/nan with placeholders
|
|
75
|
+
transformer = _SpecialFloatValuesTransformer()
|
|
76
|
+
transformed_ast_node = transformer.visit(ast_node)
|
|
77
|
+
|
|
78
|
+
# Evaluate the AST node to get the actual value.
|
|
79
|
+
data_with_placeholders = ast.literal_eval(transformed_ast_node)
|
|
80
|
+
|
|
81
|
+
# Restore placeholders to actual float values
|
|
82
|
+
return _restore_special_floats(data_with_placeholders)
|
|
83
|
+
except Exception:
|
|
84
|
+
return object_string
|
|
@@ -48,6 +48,7 @@ from ..utils.common import ensure_bytes, to_native_string
|
|
|
48
48
|
from ..utils.fips import enable_fips
|
|
49
49
|
from ..utils.tagging import GENERIC_TAGS
|
|
50
50
|
from ..utils.tracing import traced_class
|
|
51
|
+
from ._config_ast import parse as _parse_ast_config
|
|
51
52
|
|
|
52
53
|
if AGENT_RUNNING:
|
|
53
54
|
from ..log import CheckLoggingAdapter, init_logging
|
|
@@ -1488,9 +1489,4 @@ class AgentCheck(object):
|
|
|
1488
1489
|
if process.returncode != 0:
|
|
1489
1490
|
raise ValueError(f'Failed to load config: {stderr.decode()}')
|
|
1490
1491
|
|
|
1491
|
-
|
|
1492
|
-
try:
|
|
1493
|
-
return eval(decoded)
|
|
1494
|
-
# a single, literal unquoted string
|
|
1495
|
-
except Exception:
|
|
1496
|
-
return decoded
|
|
1492
|
+
return _parse_ast_config(stdout.strip().decode())
|
|
@@ -11,8 +11,9 @@ import socket
|
|
|
11
11
|
import threading
|
|
12
12
|
import time
|
|
13
13
|
from concurrent.futures.thread import ThreadPoolExecutor
|
|
14
|
+
from enum import Enum, auto
|
|
14
15
|
from ipaddress import IPv4Address
|
|
15
|
-
from typing import Any, Callable, Dict, List, Tuple # noqa: F401
|
|
16
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union # noqa: F401
|
|
16
17
|
|
|
17
18
|
from cachetools import TTLCache
|
|
18
19
|
|
|
@@ -441,3 +442,128 @@ def tracked_query(check, operation, tags=None):
|
|
|
441
442
|
yield
|
|
442
443
|
elapsed_ms = (time.time() - start_time) * 1000
|
|
443
444
|
check.histogram("dd.{}.operation.time".format(check.name), elapsed_ms, **stats_kwargs)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
class TagType(Enum):
|
|
448
|
+
"""Enum for different types of tags"""
|
|
449
|
+
|
|
450
|
+
KEYLESS = auto()
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class TagManager:
|
|
454
|
+
"""
|
|
455
|
+
Manages tags for a check. Tags are stored as a dictionary of key-value pairs
|
|
456
|
+
for key-value tags and as a list of values for keyless tags useful for easy update and deletion.
|
|
457
|
+
There's an internal cache of the tag list to avoid generating the list of tag strings
|
|
458
|
+
multiple times.
|
|
459
|
+
"""
|
|
460
|
+
|
|
461
|
+
def __init__(self) -> None:
|
|
462
|
+
self._tags: Dict[Union[str, TagType], List[str]] = {}
|
|
463
|
+
self._cached_tag_list: Optional[tuple[str, ...]] = None
|
|
464
|
+
self._keyless: TagType = TagType.KEYLESS
|
|
465
|
+
|
|
466
|
+
def set_tag(self, key: Optional[str], value: str, replace: bool = False) -> None:
|
|
467
|
+
"""
|
|
468
|
+
Set a tag with the given key and value.
|
|
469
|
+
If key is None or empty, the value is stored as a keyless tag.
|
|
470
|
+
Args:
|
|
471
|
+
key (str): The tag key, or None/empty for keyless tags
|
|
472
|
+
value (str): The tag value
|
|
473
|
+
replace (bool): If True, replaces all existing values for this key
|
|
474
|
+
If False, appends the value if it doesn't exist
|
|
475
|
+
"""
|
|
476
|
+
if not key:
|
|
477
|
+
key = self._keyless
|
|
478
|
+
|
|
479
|
+
if replace or key not in self._tags:
|
|
480
|
+
self._tags[key] = [value]
|
|
481
|
+
# Invalidate the cache since tags have changed
|
|
482
|
+
self._cached_tag_list = None
|
|
483
|
+
elif value not in self._tags[key]:
|
|
484
|
+
self._tags[key].append(value)
|
|
485
|
+
# Invalidate the cache since tags have changed
|
|
486
|
+
self._cached_tag_list = None
|
|
487
|
+
|
|
488
|
+
def set_tags_from_list(self, tag_list: List[str], replace: bool = False) -> None:
|
|
489
|
+
"""
|
|
490
|
+
Set multiple tags from a list of strings.
|
|
491
|
+
Strings can be in "key:value" format or just "value" format.
|
|
492
|
+
Args:
|
|
493
|
+
tag_list (List[str]): List of tags in "key:value" format or just "value"
|
|
494
|
+
replace (bool): If True, replaces all existing tags with the new tags list
|
|
495
|
+
"""
|
|
496
|
+
if replace:
|
|
497
|
+
self._tags.clear()
|
|
498
|
+
self._cached_tag_list = None
|
|
499
|
+
|
|
500
|
+
for tag in tag_list:
|
|
501
|
+
if ':' in tag:
|
|
502
|
+
key, value = tag.split(':', 1)
|
|
503
|
+
self.set_tag(key, value)
|
|
504
|
+
else:
|
|
505
|
+
self.set_tag(None, tag)
|
|
506
|
+
|
|
507
|
+
def delete_tag(self, key: Optional[str], value: Optional[str] = None) -> bool:
|
|
508
|
+
"""
|
|
509
|
+
Delete a tag or specific value for a tag.
|
|
510
|
+
For keyless tags, use None or empty string as the key.
|
|
511
|
+
Args:
|
|
512
|
+
key (str): The tag key to delete, or None/empty for keyless tags
|
|
513
|
+
value (str, optional): If provided, only deletes this specific value for the key.
|
|
514
|
+
If None, deletes all values for the key.
|
|
515
|
+
Returns:
|
|
516
|
+
bool: True if something was deleted, False otherwise
|
|
517
|
+
"""
|
|
518
|
+
if not key:
|
|
519
|
+
key = self._keyless
|
|
520
|
+
|
|
521
|
+
if key not in self._tags:
|
|
522
|
+
return False
|
|
523
|
+
|
|
524
|
+
if value is None:
|
|
525
|
+
# Delete the entire key
|
|
526
|
+
del self._tags[key]
|
|
527
|
+
# Invalidate the cache
|
|
528
|
+
self._cached_tag_list = None
|
|
529
|
+
return True
|
|
530
|
+
else:
|
|
531
|
+
# Delete specific value if it exists
|
|
532
|
+
if value in self._tags[key]:
|
|
533
|
+
self._tags[key].remove(value)
|
|
534
|
+
# Clean up empty lists
|
|
535
|
+
if not self._tags[key]:
|
|
536
|
+
del self._tags[key]
|
|
537
|
+
# Invalidate the cache
|
|
538
|
+
self._cached_tag_list = None
|
|
539
|
+
return True
|
|
540
|
+
return False
|
|
541
|
+
|
|
542
|
+
def _generate_tag_strings(self, tags_dict: Dict[Union[str, TagType], List[str]]) -> tuple[str, ...]:
|
|
543
|
+
"""
|
|
544
|
+
Generate a tuple of tag strings from a tags dictionary.
|
|
545
|
+
Args:
|
|
546
|
+
tags_dict (Dict[Union[str, TagType], List[str]]): Dictionary of tags to convert to strings
|
|
547
|
+
Returns:
|
|
548
|
+
tuple[str, ...]: Tuple of tag strings
|
|
549
|
+
"""
|
|
550
|
+
return tuple(
|
|
551
|
+
value if key == self._keyless else f"{key}:{value}" for key, values in tags_dict.items() for value in values
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
def get_tags(self) -> List[str]:
|
|
555
|
+
"""
|
|
556
|
+
Get a list of tag strings.
|
|
557
|
+
For key-value tags, returns "key:value" format.
|
|
558
|
+
For keyless tags, returns just the value.
|
|
559
|
+
The returned list is always sorted alphabetically.
|
|
560
|
+
Returns:
|
|
561
|
+
list: Sorted list of tag strings
|
|
562
|
+
"""
|
|
563
|
+
# Return cached list if available
|
|
564
|
+
if self._cached_tag_list is not None:
|
|
565
|
+
return list(self._cached_tag_list)
|
|
566
|
+
|
|
567
|
+
# Generate and cache regular tags
|
|
568
|
+
self._cached_tag_list = self._generate_tag_strings(self._tags)
|
|
569
|
+
return list(self._cached_tag_list)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datadog-checks-base
|
|
3
|
-
Version: 37.
|
|
3
|
+
Version: 37.15.0
|
|
4
4
|
Summary: The Datadog Check Toolkit
|
|
5
5
|
Project-URL: Source, https://github.com/DataDog/integrations-core
|
|
6
6
|
Author-email: Datadog <packages@datadoghq.com>
|
|
@@ -16,29 +16,29 @@ Provides-Extra: db
|
|
|
16
16
|
Requires-Dist: mmh3==5.1.0; extra == 'db'
|
|
17
17
|
Provides-Extra: deps
|
|
18
18
|
Requires-Dist: binary==1.0.1; extra == 'deps'
|
|
19
|
-
Requires-Dist: cachetools==
|
|
20
|
-
Requires-Dist: cryptography==
|
|
21
|
-
Requires-Dist: ddtrace==3.
|
|
19
|
+
Requires-Dist: cachetools==6.0.0; extra == 'deps'
|
|
20
|
+
Requires-Dist: cryptography==45.0.3; extra == 'deps'
|
|
21
|
+
Requires-Dist: ddtrace==3.9.1; extra == 'deps'
|
|
22
22
|
Requires-Dist: jellyfish==1.2.0; extra == 'deps'
|
|
23
23
|
Requires-Dist: lazy-loader==0.4; extra == 'deps'
|
|
24
|
-
Requires-Dist: prometheus-client==0.
|
|
25
|
-
Requires-Dist: protobuf==6.
|
|
26
|
-
Requires-Dist: pydantic==2.11.
|
|
24
|
+
Requires-Dist: prometheus-client==0.22.1; extra == 'deps'
|
|
25
|
+
Requires-Dist: protobuf==6.31.1; extra == 'deps'
|
|
26
|
+
Requires-Dist: pydantic==2.11.5; extra == 'deps'
|
|
27
27
|
Requires-Dist: python-dateutil==2.9.0.post0; extra == 'deps'
|
|
28
28
|
Requires-Dist: pywin32==310; (sys_platform == 'win32') and extra == 'deps'
|
|
29
29
|
Requires-Dist: pyyaml==6.0.2; extra == 'deps'
|
|
30
30
|
Requires-Dist: requests-toolbelt==1.0.0; extra == 'deps'
|
|
31
31
|
Requires-Dist: requests-unixsocket2==1.0.0; extra == 'deps'
|
|
32
|
-
Requires-Dist: requests==2.32.
|
|
32
|
+
Requires-Dist: requests==2.32.4; extra == 'deps'
|
|
33
33
|
Requires-Dist: simplejson==3.20.1; extra == 'deps'
|
|
34
34
|
Requires-Dist: urllib3==2.4.0; extra == 'deps'
|
|
35
35
|
Requires-Dist: wrapt==1.17.2; extra == 'deps'
|
|
36
36
|
Provides-Extra: http
|
|
37
37
|
Requires-Dist: aws-requests-auth==0.4.3; extra == 'http'
|
|
38
|
-
Requires-Dist: botocore==1.38.
|
|
38
|
+
Requires-Dist: botocore==1.38.32; extra == 'http'
|
|
39
39
|
Requires-Dist: oauthlib==3.2.2; extra == 'http'
|
|
40
40
|
Requires-Dist: pyjwt==2.10.1; extra == 'http'
|
|
41
|
-
Requires-Dist: pyopenssl==25.
|
|
41
|
+
Requires-Dist: pyopenssl==25.1.0; extra == 'http'
|
|
42
42
|
Requires-Dist: pysocks==1.7.1; extra == 'http'
|
|
43
43
|
Requires-Dist: requests-kerberos==0.15.0; extra == 'http'
|
|
44
44
|
Requires-Dist: requests-ntlm==1.3.0; extra == 'http'
|
|
@@ -3,7 +3,7 @@ datadog_checks/config.py,sha256=PrAXGdlLnoV2VMQff_noSaSJJ0wg4BAiGnw7jCQLSik,196
|
|
|
3
3
|
datadog_checks/errors.py,sha256=eFwmnrX-batIgbu-iJyseqAPNO_4rk1UuaKK89evLhg,155
|
|
4
4
|
datadog_checks/log.py,sha256=orvOgMKGNEsqSTLalCAQpWP-ouorpG1A7Gn-j2mRD80,301
|
|
5
5
|
datadog_checks/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
|
6
|
-
datadog_checks/base/__about__.py,sha256=
|
|
6
|
+
datadog_checks/base/__about__.py,sha256=FWWR7jAyvwkUM_4VgFGv7MU2D_yjzNuII5JoID5lwPQ,139
|
|
7
7
|
datadog_checks/base/__init__.py,sha256=yWegSLE-TZWIGSvAiJj9PSrUxzlOo_UVJLt2zORZ8Ek,363
|
|
8
8
|
datadog_checks/base/__init__.pyi,sha256=eH8XhrtvnD6uE6FWfEyCmKwOaaLJxNolS08D6IRHZuU,995
|
|
9
9
|
datadog_checks/base/agent.py,sha256=nX9x_BYYizRKGNYfXq5z7S0FZ9xcX_wd2tuxpGe3_8k,350
|
|
@@ -14,7 +14,8 @@ datadog_checks/base/log.py,sha256=gSfzYimmg0z3Dgmn8NpXuyMNjgXwmTbn0NcyUX8B1ls,62
|
|
|
14
14
|
datadog_checks/base/types.py,sha256=anajZS0W0TsxUHJQw-JHOP2NSeuC9BisXSy9mAStlxQ,1623
|
|
15
15
|
datadog_checks/base/checks/__init__.py,sha256=q7V6v-FwQWkQC1QWaVzKaPjZMaxPJHJcLd71C0uM7bA,211
|
|
16
16
|
datadog_checks/base/checks/__init__.pyi,sha256=LASfm-daLNQIYe6-w0NPqBw4cl83nYIX5_B-VhV6ARo,262
|
|
17
|
-
datadog_checks/base/checks/
|
|
17
|
+
datadog_checks/base/checks/_config_ast.py,sha256=v1rAhwORF80b3kfZKhf6zXZ7S5D3A2QPUK4tSo8eo-Y,3268
|
|
18
|
+
datadog_checks/base/checks/base.py,sha256=q-V8fzkJ3MMbRnhHzhE7wyuolfukXsZ_sq-Ih_PWWkw,59873
|
|
18
19
|
datadog_checks/base/checks/network.py,sha256=UijP1OVBCpCNKMy6bVkNazHgy6Sdm8qCvCYGPaWuTDo,1968
|
|
19
20
|
datadog_checks/base/checks/kube_leader/__init__.py,sha256=q7V6v-FwQWkQC1QWaVzKaPjZMaxPJHJcLd71C0uM7bA,211
|
|
20
21
|
datadog_checks/base/checks/kube_leader/__init__.pyi,sha256=UGDywoRwmCIz3Zii1uHsp7jiFGWRdn5fFMZZxgGGlQs,398
|
|
@@ -143,7 +144,7 @@ datadog_checks/base/utils/db/statement_metrics.py,sha256=U7EtERkmFzfCtfyd3094fBa
|
|
|
143
144
|
datadog_checks/base/utils/db/timed_cache.py,sha256=a9Ks5KKUvExB6GOATXTSCLamVtLD919Dn6HpweGKtFw,2114
|
|
144
145
|
datadog_checks/base/utils/db/transform.py,sha256=kNdiBZVoZm1oNRNWIatrAfgORjbkRKhs1AgW_aQCj3I,23879
|
|
145
146
|
datadog_checks/base/utils/db/types.py,sha256=OLX2Oq58JQPFBD4oqUpCLkAP7ovRGN_i1vFk1E0N8Lg,267
|
|
146
|
-
datadog_checks/base/utils/db/utils.py,sha256=
|
|
147
|
+
datadog_checks/base/utils/db/utils.py,sha256=z1CIC_VdMHHYuvoRHZKpdnk_bHj-_M07JFSPNMjIUP4,21657
|
|
147
148
|
datadog_checks/base/utils/discovery/__init__.py,sha256=vPCOdsThBcBjFJRPhDm6IsZGOwk8HlvciwCe_l8dKLk,211
|
|
148
149
|
datadog_checks/base/utils/discovery/__init__.pyi,sha256=ScVLU1Njj9ekZmewltb0cULI6BylssVHfn4CcPNeyr8,173
|
|
149
150
|
datadog_checks/base/utils/discovery/cache.py,sha256=f9L3A7YZpZ-mpZpFIwjsa5ab9cZMGkqdetdr9EpalbI,887
|
|
@@ -217,6 +218,6 @@ datadog_checks/utils/tracing.py,sha256=HQbQakKM-Lw75MDkItaYJYipS6YO24Z_ymDVxDsx5
|
|
|
217
218
|
datadog_checks/utils/prometheus/__init__.py,sha256=8WwXnM9g1sfS5267QYCJX_hd8MZl5kRgBgQ_SzdNdXs,161
|
|
218
219
|
datadog_checks/utils/prometheus/functions.py,sha256=4vWsTGLgujHwdYZo0tlAQkqDPHofqUJM3k9eItJqERQ,197
|
|
219
220
|
datadog_checks/utils/prometheus/metrics_pb2.py,sha256=xg3UdUHe4TjeR4s13LUKZ2U1WVSt6U6zjsVRG6lX6dc,173
|
|
220
|
-
datadog_checks_base-37.
|
|
221
|
-
datadog_checks_base-37.
|
|
222
|
-
datadog_checks_base-37.
|
|
221
|
+
datadog_checks_base-37.15.0.dist-info/METADATA,sha256=CUQ731u-GuA4NgGfXFBBLL5wAjuvDdbsqIC3SYgbT34,4245
|
|
222
|
+
datadog_checks_base-37.15.0.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
223
|
+
datadog_checks_base-37.15.0.dist-info/RECORD,,
|
|
File without changes
|