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.
- mongo_x_ray_hc-2.0.0/PKG-INFO +76 -0
- mongo_x_ray_hc-2.0.0/README.md +67 -0
- mongo_x_ray_hc-2.0.0/pyproject.toml +60 -0
- mongo_x_ray_hc-2.0.0/setup.cfg +4 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/__init__.py +11 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/__init__.py +9 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/base_item.py +202 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/build_info_item.py +81 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/cluster_item.py +209 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/coll_info_item.py +144 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/host_info_item.py +105 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/index_info_item.py +117 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/security_item.py +87 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/server_parameters_item.py +113 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/server_status_item.py +288 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/check_items/shard_key_item.py +78 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/framework.py +157 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/__init__.py +29 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/base_parser.py +26 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/build_info_parser.py +63 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/cache_parser.py +78 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/coll_overview_parser.py +59 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/coll_stats_parser.py +126 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/conn_parser.py +75 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/host_info_parser.py +87 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/index_info_parser.py +66 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/opcounter_parser.py +72 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/query_targeting_parser.py +46 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/rs_details_parser.py +89 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/rs_overview_parser.py +57 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/security_parser.py +93 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/sh_overview_parser.py +68 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/parsers/shard_key_parser.py +72 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/plugin.py +105 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/__init__.py +9 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/base_rule.py +32 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/cache_rule.py +133 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/chained_replication_rule.py +62 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/connections_rule.py +48 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/data_size_rule.py +95 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/fragmentation_rule.py +96 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/fs_type_rule.py +70 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/ftdc_rule.py +53 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/host_info_rule.py +49 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/index_rule.py +127 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/journaling_rule.py +48 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/numa_rule.py +42 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/op_latency_rule.py +106 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/oplog_window_rule.py +59 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/query_targeting_rule.py +66 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/rs_config_rule.py +103 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/rs_status_rule.py +75 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/sbe_rule.py +76 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/security_rule.py +73 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/shard_balance_rule.py +62 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/shard_key_rule.py +43 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/shard_mongos_rule.py +49 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/snapshot_window_rule.py +56 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/tls_protocol_rule.py +71 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/version_eol_rule.py +52 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/rules/write_concern_rule.py +57 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/shared.py +396 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/full.html +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/full.raw.html +30 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/script.js +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/script.raw.js +10 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/BuildInfoParser_1.js +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/BuildInfoParser_1.raw.js +30 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CacheParser_1.js +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CacheParser_1.raw.js +139 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollOverviewParser_1.js +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollOverviewParser_1.raw.js +82 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollStatsParser_1.js +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollStatsParser_1.raw.js +95 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/CollStatsParser_3.js +82 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/ConnParser_1.js +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/ConnParser_1.raw.js +114 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/OpcounterParser_1.js +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/healthcheck/snippets/OpcounterParser_1.raw.js +64 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc/templates/minify.sh +51 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/PKG-INFO +76 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/SOURCES.txt +87 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/dependency_links.txt +1 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/entry_points.txt +2 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/requires.txt +2 -0
- mongo_x_ray_hc-2.0.0/src/mongo_x_ray_hc.egg-info/top_level.txt +1 -0
- mongo_x_ray_hc-2.0.0/tests/test_pdf_output.py +44 -0
- mongo_x_ray_hc-2.0.0/tests/test_report_ui.py +186 -0
- 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
|
+
[](https://github.com/zhangyaoxing/mongo-x-ray-hc/actions/workflows/ci.yml)
|
|
13
|
+
[](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
|
+
[](https://github.com/zhangyaoxing/mongo-x-ray-hc/actions/workflows/ci.yml)
|
|
4
|
+
[](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,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__)
|