#!/usr/bin/env python3 import argparse from common import ( DEFAULT_CHROME_PROFILE, discover_grafana_dashboard, grafana_query, grafana_query_range, grafana_session, series_last_values, write_json, ) def find_panel_expr(discovery: dict, panel_title: str, expression_index: int) -> str: want = str(panel_title or "").strip() for panel in discovery.get("panels") or []: if str(panel.get("title") or "").strip() != want: continue targets = panel.get("targets") or [] if expression_index < 0 or expression_index >= len(targets): raise ValueError(f"expression_index out of range for panel {panel_title!r}: {expression_index}") expr = str((targets[expression_index] or {}).get("expr") or "").strip() if not expr: raise ValueError(f"empty expr for panel {panel_title!r} expression_index={expression_index}") return expr raise ValueError(f"panel not found: {panel_title!r}") def main() -> int: parser = argparse.ArgumentParser( description="Fetch metric values from an online Grafana dashboard through the Prometheus datasource proxy" ) parser.add_argument("link", help="Grafana dashboard link, or Grafana base URL when --dashboard-uid is provided") parser.add_argument("--dashboard-uid", default="", help="Grafana dashboard uid when link is only the Grafana base URL") parser.add_argument( "--profile", default=DEFAULT_CHROME_PROFILE, help="Chrome profile name for reusing an existing Grafana browser login session (default: auto).", ) parser.add_argument("--datasource-id", default="1", help="Grafana datasource proxy id, for example 1") parser.add_argument("--panel-title", required=True, help="Grafana panel title") parser.add_argument("--expression-index", type=int, default=0, help="Expression index within the selected panel") parser.add_argument("--step", default="60s", help="Query step for range queries") parser.add_argument( "--instant", action="store_true", help="Use the /query endpoint with the dashboard to/from end timestamp instead of /query_range", ) args = parser.parse_args() discovery = discover_grafana_dashboard(args.link, dashboard_uid=args.dashboard_uid or None, profile_name=args.profile) grafana_context = discovery.get("grafana_context") or {} expr = find_panel_expr(discovery, args.panel_title, args.expression_index) session = grafana_session( grafana_context.get("base_url") or "", profile_name=args.profile, path_prefix=str(grafana_context.get("path_prefix") or ""), ) if args.instant: payload = grafana_query( session, args.datasource_id, expr, time_ts=grafana_context.get("to") or grafana_context.get("from"), ) else: start_ts = grafana_context.get("from") end_ts = grafana_context.get("to") if start_ts is None or end_ts is None: raise ValueError("grafana link must include from/to when using range query mode") payload = grafana_query_range( session, args.datasource_id, expr, start_ts=int(start_ts), end_ts=int(end_ts), step=args.step, ) write_json( { "grafana_context": grafana_context, "dashboard": discovery.get("dashboard") or {}, "panel_title": args.panel_title, "expression_index": args.expression_index, "datasource_id": str(args.datasource_id), "final_promql": expr, "raw_series": payload, "last_values": series_last_values(payload), } ) return 0 if __name__ == "__main__": raise SystemExit(main())