Files
orrisroot a033dfa970
Validate / lint (push) Successful in 11m0s
Validate / import-test (push) Canceled after 1m12s
feat: bootstrap Zabbix template repository
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.
2026-11-03 14:40:19 +09:00

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())