Add production installer and observability tooling
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
VALID_ICONS = {
|
||||
"detector-smoke",
|
||||
"detector-heat",
|
||||
"module-input",
|
||||
"siren",
|
||||
"panel-nfs320",
|
||||
}
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Gera bundle versionavel de planta baixa para o Grafana")
|
||||
parser.add_argument("spec", help="Arquivo JSON com o mapa da planta")
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
help="Arquivo de saida. Default: mesmo nome com sufixo .bundle.json",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def validate_item(item: dict, seen_ids: set[str]) -> dict:
|
||||
item_id = str(item.get("id", "")).strip()
|
||||
if not item_id:
|
||||
raise ValueError("Cada item precisa de id.")
|
||||
if item_id in seen_ids:
|
||||
raise ValueError(f"Item duplicado: {item_id}")
|
||||
seen_ids.add(item_id)
|
||||
|
||||
icon = str(item.get("icon", "")).strip()
|
||||
if icon not in VALID_ICONS:
|
||||
raise ValueError(f"Icone invalido para item {item_id}: {icon}")
|
||||
|
||||
label = str(item.get("label", "")).strip()
|
||||
item_key = str(item.get("item_key", "")).strip()
|
||||
if not label:
|
||||
raise ValueError(f"Label obrigatorio para item {item_id}.")
|
||||
if not item_key:
|
||||
raise ValueError(f"item_key obrigatorio para item {item_id}.")
|
||||
|
||||
for field in ("x", "y", "width", "height"):
|
||||
value = item.get(field)
|
||||
if not isinstance(value, int) or value < 0:
|
||||
raise ValueError(f"Campo {field} invalido para item {item_id}.")
|
||||
|
||||
rotation = item.get("rotation", 0)
|
||||
if not isinstance(rotation, int):
|
||||
raise ValueError(f"rotation invalido para item {item_id}.")
|
||||
|
||||
return {
|
||||
"id": item_id,
|
||||
"label": label,
|
||||
"item_key": item_key,
|
||||
"icon": icon,
|
||||
"x": item["x"],
|
||||
"y": item["y"],
|
||||
"width": item["width"],
|
||||
"height": item["height"],
|
||||
"rotation": rotation,
|
||||
}
|
||||
|
||||
|
||||
def build_bundle(spec: dict) -> dict:
|
||||
title = str(spec.get("title", "")).strip()
|
||||
dashboard_uid = str(spec.get("grafana_dashboard_uid", "")).strip()
|
||||
background_image_url = str(spec.get("background_image_url", "")).strip()
|
||||
vfire_base_url = str(spec.get("vfire_base_url", "")).rstrip("/")
|
||||
zabbix_host = str(spec.get("zabbix_host", "")).strip()
|
||||
|
||||
if not title:
|
||||
raise ValueError("title obrigatorio.")
|
||||
if not dashboard_uid:
|
||||
raise ValueError("grafana_dashboard_uid obrigatorio.")
|
||||
if not background_image_url:
|
||||
raise ValueError("background_image_url obrigatorio.")
|
||||
if not vfire_base_url:
|
||||
raise ValueError("vfire_base_url obrigatorio.")
|
||||
if not zabbix_host:
|
||||
raise ValueError("zabbix_host obrigatorio.")
|
||||
|
||||
raw_items = spec.get("items")
|
||||
if not isinstance(raw_items, list) or not raw_items:
|
||||
raise ValueError("items deve conter ao menos um elemento.")
|
||||
|
||||
seen_ids: set[str] = set()
|
||||
items = [validate_item(item, seen_ids) for item in raw_items]
|
||||
for item in items:
|
||||
item["icon_url"] = f"{vfire_base_url}/static/grafana-icons/{item['icon']}.svg"
|
||||
item["zabbix_ref"] = {
|
||||
"host": zabbix_host,
|
||||
"item_key": item["item_key"],
|
||||
}
|
||||
|
||||
return {
|
||||
"title": title,
|
||||
"grafana_dashboard_uid": dashboard_uid,
|
||||
"background_image_url": background_image_url,
|
||||
"vfire_base_url": vfire_base_url,
|
||||
"zabbix_host": zabbix_host,
|
||||
"items": items,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
spec_path = Path(args.spec)
|
||||
output_path = Path(args.output) if args.output else spec_path.with_suffix(".bundle.json")
|
||||
|
||||
spec = json.loads(spec_path.read_text(encoding="utf-8"))
|
||||
bundle = build_bundle(spec)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
output_path.write_text(json.dumps(bundle, indent=2, ensure_ascii=False), encoding="utf-8")
|
||||
print(output_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user