Set up a repository for custom Zabbix templates and add the first template for Dell PowerVault ME storage systems. - Add conventions, version policy, and release documentation. - Add validation, index, packaging, export, and import-test scripts. - Add a disposable Zabbix test stack with version-aware imports. - Add PowerVault ME SNMP exports for Zabbix 7.0 and 8.0. - Cover system information, unit health, sensors, ports, and firmware revisions.
157 lines
4.5 KiB
Python
Executable File
157 lines
4.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Generate the template index in README.md from manifest.yaml files."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import difflib
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
TEMPLATES_DIR = ROOT / "templates"
|
|
README = ROOT / "README.md"
|
|
START = "<!-- TEMPLATES:START -->"
|
|
END = "<!-- TEMPLATES:END -->"
|
|
|
|
|
|
def load_manifests() -> list[dict]:
|
|
rows: list[dict] = []
|
|
for path in sorted(TEMPLATES_DIR.rglob("manifest.yaml")):
|
|
try:
|
|
with path.open(encoding="utf-8") as fh:
|
|
data = yaml.safe_load(fh) or {}
|
|
except yaml.YAMLError as exc:
|
|
print(f"WARN {path}: Could not parse YAML: {exc}", file=sys.stderr)
|
|
continue
|
|
|
|
if not isinstance(data, dict):
|
|
continue
|
|
|
|
zabbix = data.get("zabbix")
|
|
if not isinstance(zabbix, dict):
|
|
zabbix = {}
|
|
|
|
rows.append(
|
|
{
|
|
"id": str(data.get("id") or ""),
|
|
"name": str(data.get("name") or ""),
|
|
"version": str(data.get("version") or ""),
|
|
"description": " ".join(
|
|
str(data.get("description") or "").split()
|
|
),
|
|
"zabbix": zabbix,
|
|
"path": path.parent.relative_to(ROOT).as_posix(),
|
|
}
|
|
)
|
|
|
|
rows.sort(key=lambda row: row["id"])
|
|
return rows
|
|
|
|
|
|
def zabbix_range(zabbix: dict) -> str:
|
|
minimum = str(zabbix.get("min") or "")
|
|
maximum = zabbix.get("max")
|
|
if not minimum:
|
|
return "-"
|
|
if maximum:
|
|
return f"{minimum} - {maximum}"
|
|
return f"{minimum}+"
|
|
|
|
|
|
def tested(zabbix: dict) -> str:
|
|
values = zabbix.get("tested") or []
|
|
if not isinstance(values, list):
|
|
return "-"
|
|
return ", ".join(str(value) for value in values) or "-"
|
|
|
|
|
|
def escape_cell(value: str) -> str:
|
|
return value.replace("|", "\\|")
|
|
|
|
|
|
def render(rows: list[dict]) -> str:
|
|
if not rows:
|
|
return "_No templates yet. Add one with `make new`._\n"
|
|
|
|
lines = [
|
|
"| ID | Template | Version | Zabbix | Tested | Description |",
|
|
"|---|---|---|---|---|---|",
|
|
]
|
|
for row in rows:
|
|
lines.append(
|
|
"| [{id}]({path}/) | {name} | {version} | {zabbix} | {tested} | {description} |".format(
|
|
id=escape_cell(row["id"]),
|
|
path=row["path"],
|
|
name=escape_cell(row["name"]),
|
|
version=escape_cell(row["version"]),
|
|
zabbix=escape_cell(zabbix_range(row["zabbix"])),
|
|
tested=escape_cell(tested(row["zabbix"])),
|
|
description=escape_cell(row["description"]) or "-",
|
|
)
|
|
)
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
def replace_section(readme: str, body: str) -> str:
|
|
start = readme.find(START)
|
|
end = readme.find(END)
|
|
if start == -1 or end == -1 or end < start:
|
|
raise ValueError(f"README.md is missing {START} and/or {END}")
|
|
return readme[: start + len(START)] + "\n" + body + readme[end:]
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(
|
|
description="Generate the template index in README.md from manifest.yaml files."
|
|
)
|
|
parser.add_argument(
|
|
"--check",
|
|
action="store_true",
|
|
help="Check whether README.md is up to date without modifying it",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if not README.is_file():
|
|
print(f"ERROR README.md not found: {README}", file=sys.stderr)
|
|
return 1
|
|
|
|
original = README.read_text(encoding="utf-8")
|
|
rows = load_manifests()
|
|
body = render(rows)
|
|
|
|
try:
|
|
updated = replace_section(original, body)
|
|
except ValueError as exc:
|
|
print(f"ERROR {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
if args.check:
|
|
if original != updated:
|
|
print(
|
|
"ERROR The template index in README.md is not up to date. "
|
|
"Run `make index`.",
|
|
file=sys.stderr,
|
|
)
|
|
diff = difflib.unified_diff(
|
|
original.splitlines(),
|
|
updated.splitlines(),
|
|
fromfile="README.md",
|
|
tofile="README.md (generated)",
|
|
lineterm="",
|
|
)
|
|
print("\n".join(diff), file=sys.stderr)
|
|
return 1
|
|
print("The template index in README.md is up to date.")
|
|
return 0
|
|
|
|
README.write_text(updated, encoding="utf-8")
|
|
print(f"Updated README.md ({len(rows)} package(s)).")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|