@@ -950,6 +950,119 @@ def create_chart_visualization(workspace_id, vis_id, title, vis_type, field, ind
950950 return None
951951
952952
953+ def create_promql_dashboard_from_yaml (workspace_id , config_path , prometheus_datasource_title = "ObservabilityStack_Prometheus" ):
954+ """Create a dashboard with PromQL explore panels from a YAML config file"""
955+ import json
956+
957+ try :
958+ with open (config_path , "r" ) as f :
959+ config = yaml .safe_load (f )
960+ except (FileNotFoundError , yaml .YAMLError ) as e :
961+ print (f"⚠️ Skipping dashboard from { config_path } : { e } " )
962+ return None
963+
964+ dashboard_config = config .get ("dashboard" , {})
965+ panel_defs = config .get ("panels" , [])
966+ dashboard_id = dashboard_config .get ("id" , "promql-dashboard" )
967+
968+ print (f"📊 Creating { dashboard_config .get ('title' , 'PromQL Dashboard' )} dashboard ({ len (panel_defs )} panels)..." )
969+
970+ viz_template = json .dumps ({
971+ "title" : "" , "chartType" : "line" ,
972+ "params" : {
973+ "addLegend" : True , "addTimeMarker" : False , "legendPosition" : "bottom" ,
974+ "legendTitle" : "" , "lineMode" : "straight" , "lineStyle" : "line" , "lineWidth" : 2 ,
975+ "showFullTimeRange" : False , "standardAxes" : [],
976+ "thresholdOptions" : {"baseColor" : "#00BD6B" , "thresholds" : [], "thresholdStyle" : "off" },
977+ "titleOptions" : {"show" : False , "titleName" : "" },
978+ "tooltipOptions" : {"mode" : "all" }
979+ },
980+ "axesMapping" : {"color" : "Series" , "x" : "Time" , "y" : "Value" }
981+ })
982+
983+ dataset = {
984+ "id" : prometheus_datasource_title , "title" : prometheus_datasource_title ,
985+ "type" : "PROMETHEUS" , "language" : "PROMQL" , "timeFieldName" : "Time" ,
986+ "dataSource" : {}, "signalType" : "metrics"
987+ }
988+
989+ created_ids = []
990+ for panel_def in panel_defs :
991+ panel_id = panel_def ["id" ]
992+ search_source = json .dumps ({
993+ "query" : {"query" : panel_def ["query" ], "language" : "PROMQL" , "dataset" : dataset },
994+ "filter" : [], "indexRefName" : "kibanaSavedObjectMeta.searchSourceJSON.index"
995+ })
996+ payload = {
997+ "attributes" : {
998+ "title" : panel_def ["title" ], "description" : "" , "hits" : 0 ,
999+ "columns" : ["_source" ], "sort" : [], "version" : 1 , "type" : "metrics" ,
1000+ "visualization" : viz_template ,
1001+ "uiState" : json .dumps ({"activeTab" : "explore_visualization_tab" }),
1002+ "kibanaSavedObjectMeta" : {"searchSourceJSON" : search_source }
1003+ },
1004+ "references" : [{"name" : "kibanaSavedObjectMeta.searchSourceJSON.index" , "type" : "index-pattern" , "id" : prometheus_datasource_title }]
1005+ }
1006+ if workspace_id and workspace_id != "default" :
1007+ payload ["workspaces" ] = [workspace_id ]
1008+ url = f"{ BASE_URL } /w/{ workspace_id } /api/saved_objects/explore/{ panel_id } "
1009+ else :
1010+ url = f"{ BASE_URL } /api/saved_objects/explore/{ panel_id } "
1011+ try :
1012+ response = requests .post (url , auth = (USERNAME , PASSWORD ), headers = {"Content-Type" : "application/json" , "osd-xsrf" : "true" }, json = payload , verify = False , timeout = 10 )
1013+ if response .status_code == 200 :
1014+ created_ids .append (panel_id )
1015+ print (f" ✅ { panel_def ['title' ]} " )
1016+ elif response .status_code == 409 :
1017+ requests .put (url , auth = (USERNAME , PASSWORD ), headers = {"Content-Type" : "application/json" , "osd-xsrf" : "true" }, json = {"attributes" : payload ["attributes" ], "references" : payload ["references" ]}, verify = False , timeout = 10 )
1018+ created_ids .append (panel_id )
1019+ print (f" 🔄 { panel_def ['title' ]} (updated)" )
1020+ else :
1021+ print (f" ⚠️ { panel_def ['title' ]} : { response .status_code } { response .text [:100 ]} " )
1022+ except requests .exceptions .RequestException as e :
1023+ print (f" ⚠️ { panel_def ['title' ]} : { e } " )
1024+
1025+ if not created_ids :
1026+ print ("⚠️ No panels created, skipping dashboard" )
1027+ return None
1028+
1029+ panels = []
1030+ references = []
1031+ for i , pid in enumerate (created_ids ):
1032+ panels .append ({"version" : "3.6.0" , "panelIndex" : pid , "gridData" : {"i" : pid , "x" : (i % 2 ) * 24 , "y" : (i // 2 ) * 15 , "w" : 24 , "h" : 15 }, "panelRefName" : f"panel_{ i } " })
1033+ references .append ({"name" : f"panel_{ i } " , "type" : "explore" , "id" : pid })
1034+
1035+ dashboard_payload = {
1036+ "attributes" : {
1037+ "title" : dashboard_config .get ("title" , "PromQL Dashboard" ),
1038+ "description" : dashboard_config .get ("description" , "" ),
1039+ "panelsJSON" : json .dumps (panels ),
1040+ "optionsJSON" : json .dumps ({"useMargins" : True , "hidePanelTitles" : False }),
1041+ "timeRestore" : False ,
1042+ "kibanaSavedObjectMeta" : {"searchSourceJSON" : json .dumps ({})}
1043+ },
1044+ "references" : references
1045+ }
1046+ if workspace_id and workspace_id != "default" :
1047+ dashboard_payload ["workspaces" ] = [workspace_id ]
1048+ url = f"{ BASE_URL } /w/{ workspace_id } /api/saved_objects/dashboard/{ dashboard_id } "
1049+ else :
1050+ url = f"{ BASE_URL } /api/saved_objects/dashboard/{ dashboard_id } "
1051+ try :
1052+ # Always delete and recreate the dashboard so panel order matches YAML
1053+ requests .delete (url , auth = (USERNAME , PASSWORD ), headers = {"osd-xsrf" : "true" }, verify = False , timeout = 10 )
1054+ response = requests .post (url , auth = (USERNAME , PASSWORD ), headers = {"Content-Type" : "application/json" , "osd-xsrf" : "true" }, json = dashboard_payload , verify = False , timeout = 10 )
1055+ if response .status_code == 200 :
1056+ print (f"✅ Created { dashboard_config ['title' ]} dashboard ({ len (created_ids )} panels)" )
1057+ return dashboard_id
1058+ else :
1059+ print (f"⚠️ Dashboard creation failed: { response .text [:200 ]} " )
1060+ return None
1061+ except requests .exceptions .RequestException as e :
1062+ print (f"⚠️ Error creating dashboard: { e } " )
1063+ return None
1064+
1065+
9531066def create_overview_dashboard (workspace_id ):
9541067 """Create an overview landing dashboard with markdown links to all observability features"""
9551068 import json
@@ -1172,6 +1285,10 @@ def main():
11721285 # Create overview landing dashboard (becomes the new default)
11731286 create_overview_dashboard (workspace_id )
11741287
1288+ # Create self-monitoring dashboards (PromQL explore panels)
1289+ create_promql_dashboard_from_yaml (workspace_id , "/config/dashboard-pipeline-health.yaml" )
1290+ create_promql_dashboard_from_yaml (workspace_id , "/config/dashboard-opensearch-health.yaml" )
1291+
11751292 # Create saved queries for common agent observability patterns
11761293 create_default_saved_queries (workspace_id )
11771294
0 commit comments