mongo-x-ray-hc 2.0.0__tar.gz

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 (89) hide show
  1. mongo_x_ray_hc-2.0.0/PKG-INFO +76 -0
  2. mongo_x_ray_hc-2.0.0/README.md +67 -0
  3. mongo_x_ray_hc-2.0.0/pyproject.toml +60 -0
  4. mongo_x_ray_hc-2.0.0/setup.cfg +4 -0
  5. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/__init__.py +11 -0
  6. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/__init__.py +9 -0
  7. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/base_item.py +202 -0
  8. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/build_info_item.py +81 -0
  9. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/cluster_item.py +209 -0
  10. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/coll_info_item.py +144 -0
  11. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/host_info_item.py +105 -0
  12. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/index_info_item.py +117 -0
  13. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/security_item.py +87 -0
  14. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/server_parameters_item.py +113 -0
  15. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/server_status_item.py +288 -0
  16. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/shard_key_item.py +78 -0
  17. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/framework.py +157 -0
  18. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/__init__.py +29 -0
  19. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/base_parser.py +26 -0
  20. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/build_info_parser.py +63 -0
  21. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/cache_parser.py +78 -0
  22. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/coll_overview_parser.py +59 -0
  23. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/coll_stats_parser.py +126 -0
  24. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/conn_parser.py +75 -0
  25. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/host_info_parser.py +87 -0
  26. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/index_info_parser.py +66 -0
  27. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/opcounter_parser.py +72 -0
  28. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/query_targeting_parser.py +46 -0
  29. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/rs_details_parser.py +89 -0
  30. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/rs_overview_parser.py +57 -0
  31. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/security_parser.py +93 -0
  32. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/sh_overview_parser.py +68 -0
  33. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/shard_key_parser.py +72 -0
  34. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/plugin.py +105 -0
  35. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/__init__.py +9 -0
  36. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/base_rule.py +32 -0
  37. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/cache_rule.py +133 -0
  38. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/chained_replication_rule.py +62 -0
  39. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/connections_rule.py +48 -0
  40. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/data_size_rule.py +95 -0
  41. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/fragmentation_rule.py +96 -0
  42. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/fs_type_rule.py +70 -0
  43. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/ftdc_rule.py +53 -0
  44. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/host_info_rule.py +49 -0
  45. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/index_rule.py +127 -0
  46. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/journaling_rule.py +48 -0
  47. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/numa_rule.py +42 -0
  48. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/op_latency_rule.py +106 -0
  49. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/oplog_window_rule.py +59 -0
  50. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/query_targeting_rule.py +66 -0
  51. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/rs_config_rule.py +103 -0
  52. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/rs_status_rule.py +75 -0
  53. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/sbe_rule.py +76 -0
  54. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/security_rule.py +73 -0
  55. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/shard_balance_rule.py +62 -0
  56. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/shard_key_rule.py +43 -0
  57. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/shard_mongos_rule.py +49 -0
  58. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/snapshot_window_rule.py +56 -0
  59. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/tls_protocol_rule.py +71 -0
  60. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/version_eol_rule.py +52 -0
  61. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/write_concern_rule.py +57 -0
  62. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/shared.py +396 -0
  63. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/full.html +1 -0
  64. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/full.raw.html +30 -0
  65. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/script.js +1 -0
  66. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/script.raw.js +10 -0
  67. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/BuildInfoParser_1.js +1 -0
  68. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/BuildInfoParser_1.raw.js +30 -0
  69. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CacheParser_1.js +1 -0
  70. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CacheParser_1.raw.js +139 -0
  71. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollOverviewParser_1.js +1 -0
  72. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollOverviewParser_1.raw.js +82 -0
  73. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollStatsParser_1.js +1 -0
  74. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollStatsParser_1.raw.js +95 -0
  75. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollStatsParser_3.js +82 -0
  76. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/ConnParser_1.js +1 -0
  77. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/ConnParser_1.raw.js +114 -0
  78. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/OpcounterParser_1.js +1 -0
  79. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/OpcounterParser_1.raw.js +64 -0
  80. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/minify.sh +51 -0
  81. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/PKG-INFO +76 -0
  82. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/SOURCES.txt +87 -0
  83. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/dependency_links.txt +1 -0
  84. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/entry_points.txt +2 -0
  85. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/requires.txt +2 -0
  86. mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/top_level.txt +1 -0
  87. mongo_x_ray_hc-2.0.0/tests/test_pdf_output.py +44 -0
  88. mongo_x_ray_hc-2.0.0/tests/test_report_ui.py +186 -0
  89. mongo_x_ray_hc-2.0.0/tests/test_shared.py +361 -0
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.4
2
+ Name: mongo-x-ray-hc
3
+ Version: 2.0.0
4
+ Summary: Health check analysis plugin for x-ray
5
+ Requires-Python: <4,>=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: mongo-x-ray>=2.0.0
8
+ Requires-Dist: pymongo>=4.0
9
+
10
+ # mongo-x-ray-hc
11
+
12
+ [![CI](https://github.com/zhangyaoxing/mongo-x-ray-hc/actions/workflows/ci.yml/badge.svg)](https://github.com/zhangyaoxing/mongo-x-ray-hc/actions/workflows/ci.yml)
13
+ [![PyPI](https://img.shields.io/pypi/v/mongo-x-ray-hc.svg)](https://pypi.org/project/mongo-x-ray-hc/)
14
+
15
+ Health check analysis plugin for [x-ray](https://github.com/mongodb-ps/ce-mongo-x-ray).
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pip install mongo-x-ray mongo-x-ray-hc
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```bash
26
+ x-ray healthcheck
27
+ x-ray hc # alias for healthcheck
28
+ x-ray healthcheck -u mongodb://localhost:27017 -f html -o /path/to/output/
29
+ ```
30
+
31
+ ## Compatibility
32
+
33
+ Supports MongoDB 5.0 and above on replica sets and sharded clusters.
34
+ **Standalone deployments are not supported.**
35
+
36
+ | Replica Set | Sharded Cluster | Standalone |
37
+ | :---------: | :-------------: | :--------: |
38
+ | ✅ | ✅ | ❌ |
39
+
40
+ ## Parameters
41
+
42
+ ```bash
43
+ x-ray healthcheck [-h] [-u URI] [-s CHECKSET] [-o OUTPUT]
44
+ [-f {markdown,html,pdf}] [--no-browser]
45
+ ```
46
+
47
+ | Argument | Description | Default |
48
+ | --- | --- | --- |
49
+ | `-u, --uri` | MongoDB connection URI. | `HC_URI` env var, else `mongodb://localhost:47017` |
50
+ | `-s, --checkset` | Checkset to run. | `default` |
51
+ | `-o, --output` | Output folder path. | `output/` |
52
+ | `-f, --format` | Output format: `markdown`, `html` or `pdf` (PDF also keeps Markdown and HTML). | `html` |
53
+ | `--no-browser` | Do not open the generated report in the browser. | `false` |
54
+
55
+ ## Check Items
56
+
57
+ | Item | Purpose |
58
+ | --- | --- |
59
+ | `BuildInfoItem` | MongoDB server build information, including a version end-of-life check. |
60
+ | `ClusterItem` | Cluster topology: replica set config/status, sharding overview and oplog window. |
61
+ | `CollInfoItem` | Collection information: sizes, fragmentation and operation latency. |
62
+ | `HostInfoItem` | Host information: filesystem type, NUMA settings and host properties. |
63
+ | `IndexInfoItem` | Index coverage and health. |
64
+ | `SecurityItem` | Security posture (authentication and authorization). |
65
+ | `ServerStatusItem` | Server status: cache, connections, opcounters and query targeting. |
66
+ | `ShardKeyItem` | Shard key selection and data balance. |
67
+
68
+ ## Development
69
+
70
+ Requires Python 3.10+, MongoDB 5.0 or later, and the [mongo-x-ray](https://github.com/mongodb-ps/ce-mongo-x-ray) core package.
71
+
72
+ ```bash
73
+ make unit-test # run the unit tests
74
+ make lint # ruff check + ruff format --check
75
+ make minify # minify templates
76
+ ```
@@ -0,0 +1,67 @@
1
+ # mongo-x-ray-hc
2
+
3
+ [![CI](https://github.com/zhangyaoxing/mongo-x-ray-hc/actions/workflows/ci.yml/badge.svg)](https://github.com/zhangyaoxing/mongo-x-ray-hc/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/mongo-x-ray-hc.svg)](https://pypi.org/project/mongo-x-ray-hc/)
5
+
6
+ Health check analysis plugin for [x-ray](https://github.com/mongodb-ps/ce-mongo-x-ray).
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pip install mongo-x-ray mongo-x-ray-hc
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```bash
17
+ x-ray healthcheck
18
+ x-ray hc # alias for healthcheck
19
+ x-ray healthcheck -u mongodb://localhost:27017 -f html -o /path/to/output/
20
+ ```
21
+
22
+ ## Compatibility
23
+
24
+ Supports MongoDB 5.0 and above on replica sets and sharded clusters.
25
+ **Standalone deployments are not supported.**
26
+
27
+ | Replica Set | Sharded Cluster | Standalone |
28
+ | :---------: | :-------------: | :--------: |
29
+ | ✅ | ✅ | ❌ |
30
+
31
+ ## Parameters
32
+
33
+ ```bash
34
+ x-ray healthcheck [-h] [-u URI] [-s CHECKSET] [-o OUTPUT]
35
+ [-f {markdown,html,pdf}] [--no-browser]
36
+ ```
37
+
38
+ | Argument | Description | Default |
39
+ | --- | --- | --- |
40
+ | `-u, --uri` | MongoDB connection URI. | `HC_URI` env var, else `mongodb://localhost:47017` |
41
+ | `-s, --checkset` | Checkset to run. | `default` |
42
+ | `-o, --output` | Output folder path. | `output/` |
43
+ | `-f, --format` | Output format: `markdown`, `html` or `pdf` (PDF also keeps Markdown and HTML). | `html` |
44
+ | `--no-browser` | Do not open the generated report in the browser. | `false` |
45
+
46
+ ## Check Items
47
+
48
+ | Item | Purpose |
49
+ | --- | --- |
50
+ | `BuildInfoItem` | MongoDB server build information, including a version end-of-life check. |
51
+ | `ClusterItem` | Cluster topology: replica set config/status, sharding overview and oplog window. |
52
+ | `CollInfoItem` | Collection information: sizes, fragmentation and operation latency. |
53
+ | `HostInfoItem` | Host information: filesystem type, NUMA settings and host properties. |
54
+ | `IndexInfoItem` | Index coverage and health. |
55
+ | `SecurityItem` | Security posture (authentication and authorization). |
56
+ | `ServerStatusItem` | Server status: cache, connections, opcounters and query targeting. |
57
+ | `ShardKeyItem` | Shard key selection and data balance. |
58
+
59
+ ## Development
60
+
61
+ Requires Python 3.10+, MongoDB 5.0 or later, and the [mongo-x-ray](https://github.com/mongodb-ps/ce-mongo-x-ray) core package.
62
+
63
+ ```bash
64
+ make unit-test # run the unit tests
65
+ make lint # ruff check + ruff format --check
66
+ make minify # minify templates
67
+ ```
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ requires = ["setuptools==83.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mongo-x-ray-hc"
7
+ version = "2.0.0"
8
+ description = "Health check analysis plugin for x-ray"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10,<4"
11
+ dependencies = [
12
+ "mongo-x-ray>=2.0.0",
13
+ "pymongo>=4.0",
14
+ ]
15
+
16
+ [project.entry-points."mongo_x_ray.plugins"]
17
+ healthcheck = "mongo_x_ray_hc.plugin:HealthcheckPlugin"
18
+
19
+ [tool.setuptools.packages.find]
20
+ where = ["src"]
21
+ include = ["mongo_x_ray_hc*"]
22
+
23
+ [tool.setuptools.package-data]
24
+ "mongo_x_ray_hc" = ["templates/**/*"]
25
+
26
+ [tool.pytest.ini_options]
27
+ testpaths = ["tests"]
28
+ pythonpath = ["src"]
29
+ python_files = ["test_*.py"]
30
+ python_classes = ["Test*"]
31
+ python_functions = ["test_*"]
32
+ markers = [
33
+ "slow: marks tests as slow",
34
+ "integration: marks tests as integration tests",
35
+ "unit: marks tests as unit tests",
36
+ ]
37
+
38
+
39
+ [tool.ruff]
40
+ line-length = 120
41
+ target-version = "py310"
42
+ exclude = [".venv", "build", "dist"]
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E", "F", "I"]
46
+ ignore = ["E501"]
47
+
48
+ [tool.ruff.lint.isort]
49
+ known-first-party = ["mongo_x_ray", "mongo_x_ray_ftdc", "mongo_x_ray_gmd", "mongo_x_ray_hc", "mongo_x_ray_log", "mongo_x_ray_risk"]
50
+
51
+
52
+ [tool.pyright]
53
+ pythonVersion = "3.10"
54
+ venvPath = "../ce-mongo-x-ray"
55
+ venv = ".venv"
56
+ typeCheckingMode = "basic"
57
+ extraPaths = [
58
+ "src",
59
+ "../ce-mongo-x-ray/src",
60
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,11 @@
1
+ """
2
+ Copyright (c) 2026 MongoDB Inc.
3
+
4
+ DISCLAIMER: THESE CODE SAMPLES ARE PROVIDED FOR EDUCATIONAL AND ILLUSTRATIVE PURPOSES ONLY,
5
+ TO DEMONSTRATE THE FUNCTIONALITY OF SPECIFIC MONGODB FEATURES.
6
+ THEY ARE NOT PRODUCTION-READY AND MAY LACK THE SECURITY HARDENING, ERROR HANDLING, AND TESTING REQUIRED FOR A LIVE ENVIRONMENT.
7
+ YOU ARE RESPONSIBLE FOR TESTING, VALIDATING, AND SECURING THIS CODE WITHIN YOUR OWN ENVIRONMENT BEFORE IMPLEMENTATION.
8
+ THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OR LIABILITY.
9
+
10
+ MongoDB health check analysis package.
11
+ """
@@ -0,0 +1,9 @@
1
+ """
2
+ Copyright (c) 2026 MongoDB Inc.
3
+
4
+ DISCLAIMER: THESE CODE SAMPLES ARE PROVIDED FOR EDUCATIONAL AND ILLUSTRATIVE PURPOSES ONLY,
5
+ TO DEMONSTRATE THE FUNCTIONALITY OF SPECIFIC MONGODB FEATURES.
6
+ THEY ARE NOT PRODUCTION-READY AND MAY LACK THE SECURITY HARDENING, ERROR HANDLING, AND TESTING REQUIRED FOR A LIVE ENVIRONMENT.
7
+ YOU ARE RESPONSIBLE FOR TESTING, VALIDATING, AND SECURING THIS CODE WITHIN YOUR OWN ENVIRONMENT BEFORE IMPLEMENTATION.
8
+ THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OR LIABILITY.
9
+ """
@@ -0,0 +1,202 @@
1
+ """
2
+ Copyright (c) 2026 MongoDB Inc.
3
+
4
+ DISCLAIMER: THESE CODE SAMPLES ARE PROVIDED FOR EDUCATIONAL AND ILLUSTRATIVE PURPOSES ONLY,
5
+ TO DEMONSTRATE THE FUNCTIONALITY OF SPECIFIC MONGODB FEATURES.
6
+ THEY ARE NOT PRODUCTION-READY AND MAY LACK THE SECURITY HARDENING, ERROR HANDLING, AND TESTING REQUIRED FOR A LIVE ENVIRONMENT.
7
+ YOU ARE RESPONSIBLE FOR TESTING, VALIDATING, AND SECURING THIS CODE WITHIN YOUR OWN ENVIRONMENT BEFORE IMPLEMENTATION.
8
+ THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OR LIABILITY.
9
+ """
10
+
11
+ import gzip
12
+ import html as html_mod
13
+ import logging
14
+ import os
15
+ from abc import ABC, abstractmethod
16
+ from typing import Optional, Union
17
+
18
+ from bson import json_util
19
+
20
+ from mongo_x_ray.parsers.base_parser import TABLE_ALIGNMENT
21
+ from mongo_x_ray.shared import SEVERITY, to_json
22
+ from mongo_x_ray.utils import env, get_script_path, to_ejson
23
+ from mongo_x_ray_hc.rules.base_rule import BaseRule
24
+
25
+
26
+ def colorize_severity(severity: SEVERITY) -> str:
27
+ mapping = {
28
+ SEVERITY.HIGH.name: "red",
29
+ SEVERITY.MEDIUM.name: "orange",
30
+ SEVERITY.LOW.name: "green",
31
+ SEVERITY.INFO.name: "gray",
32
+ }
33
+ return mapping.get(severity.name, "black")
34
+
35
+
36
+ class BaseItem(ABC):
37
+ _name: str
38
+ _test_result: list
39
+ _config: dict
40
+
41
+ def __init__(self, output_folder: str, config: Optional[dict] = None, **_kwargs) -> None:
42
+ self._config: dict = config or {}
43
+ self._test_result: list = []
44
+ self._logger = logging.getLogger(self.__class__.__name__)
45
+ self._output_folder: str = output_folder if output_folder.endswith("/") else f"{output_folder}/"
46
+ self._rules: dict[str, BaseRule] = {}
47
+
48
+ @abstractmethod
49
+ def test(self, *args, **kwargs) -> None:
50
+ pass
51
+
52
+ @property
53
+ def name(self):
54
+ return self._name
55
+
56
+ @property
57
+ def description(self) -> str:
58
+ desc: str = ""
59
+ for rule in self._rules.values():
60
+ desc += rule.description_md + "\n"
61
+ return desc
62
+
63
+ @property
64
+ def captured_sample(self) -> Optional[Union[dict, list]]:
65
+ try:
66
+ if self.cache_file_name.endswith(".gz"):
67
+ with gzip.open(self.cache_file_name, "rt") as f:
68
+ return json_util.loads(f.read())
69
+ else:
70
+ with open(self.cache_file_name, "r", encoding="utf-8") as f:
71
+ return json_util.loads(f.read())
72
+ except FileNotFoundError:
73
+ return None
74
+
75
+ @captured_sample.setter
76
+ def captured_sample(self, data: Union[dict, list]) -> None:
77
+ if self.cache_file_name.endswith(".gz"):
78
+ with gzip.open(self.cache_file_name, "wt") as f:
79
+ f.write(to_ejson(data))
80
+ else:
81
+ with open(self.cache_file_name, "w", encoding="utf-8") as f:
82
+ f.write(to_ejson(data))
83
+
84
+ @property
85
+ def test_result(self) -> dict:
86
+ return {
87
+ "name": self.name,
88
+ "description": self.description,
89
+ "items": self._test_result,
90
+ }
91
+
92
+ @property
93
+ def test_result_markdown(self) -> str:
94
+ result = ""
95
+ if len(self._test_result) == 0:
96
+ result += "<b style='color: green;'>Pass.</b>\n\n"
97
+ return result
98
+
99
+ result += (
100
+ '| <span data-sortable="false">\\#</span>{60px}'
101
+ ' | <span data-sortable="true">Host</span>{180px}'
102
+ ' | <span data-sortable="true">Severity</span>{120px}'
103
+ ' | <span data-sortable="true">Category</span>{200px}'
104
+ ' | <span data-sortable="false">Message</span>{*} |\n'
105
+ )
106
+ result += "|:----------:|:----------:|:----------:|---------|---------|\n"
107
+ for idx, item in enumerate(self._test_result):
108
+ severity = item["severity"]
109
+ severity_cell = (
110
+ f'<span data-sort-value="{severity.value}">'
111
+ f"<b style='color: {colorize_severity(severity)}'>"
112
+ f" {severity.name} </b></span>"
113
+ )
114
+ category_cell = item["title"]
115
+ risk = item.get("matched_risk")
116
+ if risk:
117
+ risk_id = html_mod.escape(str(risk.get("id", "")))
118
+ risk_name = html_mod.escape(str(risk.get("name", ""))).replace("\r\n", "<br>").replace("\n", "<br>")
119
+ risk_desc = (
120
+ html_mod.escape(str(risk.get("description", ""))).replace("\r\n", "<br>").replace("\n", "<br>")
121
+ )
122
+ category_cell += (
123
+ f' <span class="risk-badge">RISK-{risk_id}'
124
+ f'<span class="risk-tooltip">'
125
+ f'<span class="risk-name">{risk_name}</span>'
126
+ f"{risk_desc}"
127
+ f"</span></span>"
128
+ )
129
+ result += f"| **{idx + 1}** | `{item['host']}` | {severity_cell} | {category_cell} | {item['message']} |\n"
130
+ result += "\n"
131
+ return result
132
+
133
+ @property
134
+ def review_result(self) -> dict:
135
+ return {"name": self.name, "description": self.description, "data": []}
136
+
137
+ @property
138
+ def review_result_markdown(self) -> str:
139
+ result_data = self.review_result["data"]
140
+ result = ""
141
+ if len(result_data) == 0:
142
+ result += "(No data)\n\n"
143
+ return result
144
+ i = 0
145
+ for j, block in enumerate(result_data):
146
+ chart_type = block.get("type")
147
+ caption = block.get("caption")
148
+ notes = block.get("notes", "")
149
+ if chart_type == "table":
150
+ result += f"#### ({i + 1}) {caption}\n"
151
+ result += f"{notes}\n"
152
+ columns = block.get("columns", [])
153
+ header_parts = []
154
+ for col in columns:
155
+ name = col.get("name", "(NOT SET)")
156
+ sortable = col.get("sortable", True)
157
+ header_parts.append(f'<span data-sortable="{"true" if sortable else "false"}">{name}</span>')
158
+ align = [TABLE_ALIGNMENT.get(col.get("align", "center"), TABLE_ALIGNMENT["center"]) for col in columns]
159
+ result += f"|{'|'.join(header_parts)}|\n"
160
+ result += f"|{'|'.join(align)}|\n"
161
+ for row in block.get("rows", []):
162
+ row_text = []
163
+ for cell in row:
164
+ if isinstance(cell, tuple):
165
+ display, sort_value = cell
166
+ row_text.append(
167
+ f'<span data-sort-value="{html_mod.escape(str(sort_value), quote=True)}">{display}</span>'
168
+ )
169
+ else:
170
+ row_text.append(str(cell))
171
+ result += "|" + "|".join(row_text) + "|\n"
172
+ result += "\n"
173
+ i += 1
174
+ elif chart_type == "chart":
175
+ result += f'<div id="container_{self.__class__.__name__}_{j}"></div>'
176
+ result += "<script type='text/javascript'>\n"
177
+ result += "(function() {\n"
178
+ result += f"const container = document.getElementById('container_{self.__class__.__name__}_{j}');\n"
179
+ result += f"let data = {to_json(block.get('data'))};\n"
180
+ file_name = f"{self.__class__.__name__}_{j}.js"
181
+ file_path = os.path.join("templates", "healthcheck", "snippets", file_name)
182
+ file_path = get_script_path(file_path, package="mongo_x_ray_hc")
183
+ if os.path.exists(file_path):
184
+ with open(file_path, "r", encoding="utf-8") as js_file:
185
+ for line in js_file:
186
+ result += line.replace("{name}", self.__class__.__name__)
187
+ result += "})()\n"
188
+ result += "</script>\n"
189
+ return result
190
+
191
+ @property
192
+ def cache_file_name(self) -> str:
193
+ if env == "development":
194
+ return f"{self._output_folder}{self.__class__.__name__}_raw.json"
195
+ return f"{self._output_folder}{self.__class__.__name__}_raw.json.gz"
196
+
197
+ def append_test_result(self, host: str, severity: SEVERITY, title: str, message: str) -> None:
198
+ self._test_result.append({"host": host, "severity": severity, "title": title, "message": message})
199
+
200
+ def append_test_results(self, items: list) -> None:
201
+ for item in items:
202
+ self.append_test_result(item["host"], item["severity"], item["title"], item["description"])
@@ -0,0 +1,81 @@
1
+ """
2
+ Copyright (c) 2026 MongoDB Inc.
3
+
4
+ DISCLAIMER: THESE CODE SAMPLES ARE PROVIDED FOR EDUCATIONAL AND ILLUSTRATIVE PURPOSES ONLY,
5
+ TO DEMONSTRATE THE FUNCTIONALITY OF SPECIFIC MONGODB FEATURES.
6
+ THEY ARE NOT PRODUCTION-READY AND MAY LACK THE SECURITY HARDENING, ERROR HANDLING, AND TESTING REQUIRED FOR A LIVE ENVIRONMENT.
7
+ YOU ARE RESPONSIBLE FOR TESTING, VALIDATING, AND SECURING THIS CODE WITHIN YOUR OWN ENVIRONMENT BEFORE IMPLEMENTATION.
8
+ THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OR LIABILITY.
9
+ """
10
+
11
+ from typing import Optional
12
+
13
+ from mongo_x_ray.utils import yellow
14
+ from mongo_x_ray.version import Version
15
+ from mongo_x_ray_hc.check_items.base_item import BaseItem
16
+ from mongo_x_ray_hc.parsers.build_info_parser import BuildInfoParser
17
+ from mongo_x_ray_hc.rules.version_eol_rule import VersionEOLRule
18
+ from mongo_x_ray_hc.shared import MAX_MONGOS_PING_LATENCY, discover_nodes, enum_all_nodes, enum_result_items
19
+
20
+
21
+ class BuildInfoItem(BaseItem):
22
+ """Build Info Check Item Module. Used to check MongoDB server build information."""
23
+
24
+ def __init__(self, output_folder: str, config: Optional[dict] = None):
25
+ super().__init__(output_folder, config)
26
+ self._name = "Build Information"
27
+ self._rules["version_eol"] = VersionEOLRule(config)
28
+
29
+ def test(self, *args, **kwargs) -> None:
30
+ client = kwargs.get("client")
31
+ parsed_uri = kwargs.get("parsed_uri")
32
+ nodes = discover_nodes(client, parsed_uri)
33
+
34
+ def func_node(set_name, node, **kwargs):
35
+ host = node["host"]
36
+ if "pingLatencySec" in node and node["pingLatencySec"] > MAX_MONGOS_PING_LATENCY:
37
+ self._logger.warning(
38
+ yellow(
39
+ f"Skip {host} because it has been irresponsive for {node['pingLatencySec'] / 60:.2f} minutes."
40
+ )
41
+ )
42
+ return None, None
43
+ client = node["client"]
44
+ raw_result = client.admin.command("buildInfo")
45
+ test_result, _ = self._rules["version_eol"].apply(raw_result, extra_info={"host": host})
46
+ running_version = Version(raw_result.get("versionArray", None))
47
+ node["version"] = running_version
48
+ self.append_test_results(test_result)
49
+
50
+ return test_result, raw_result
51
+
52
+ raw_result = enum_all_nodes(
53
+ nodes,
54
+ func_mongos_member=func_node,
55
+ func_rs_member=func_node,
56
+ func_shard_member=func_node,
57
+ func_config_member=func_node,
58
+ )
59
+
60
+ self.captured_sample = raw_result
61
+
62
+ @property
63
+ def review_result_markdown(self):
64
+ result = self.captured_sample
65
+ build_infos = []
66
+
67
+ def func_node(name, node, **kwargs):
68
+ raw_result = node.get("rawResult", {})
69
+ host = node["host"]
70
+ build_infos.append((name, host, raw_result))
71
+
72
+ enum_result_items(
73
+ result,
74
+ func_mongos_member=func_node,
75
+ func_rs_member=func_node,
76
+ func_shard_member=func_node,
77
+ func_config_member=func_node,
78
+ )
79
+ parser = BuildInfoParser()
80
+
81
+ return parser.markdown(build_infos, caller=self.__class__.__name__)