Dashboard Migrations v16: Grid layout migration (#109603)
* Migration to be verified: v17 Convert minSpan to maxPerRow in panels * Address review comments: use Go idiom for variable naming and sort.Ints for sorting * Migration to be verified: 16 Grid layout migration * Refactor v17 and v19 migrations to use shared helper functions * Address reviews comments --------- Co-authored-by: Haris Rozajac <58232930+harisrozajac@users.noreply.github.com> Co-authored-by: Haris Rozajac <haris.rozajac12@gmail.com>
This commit is contained in:
co-authored by
Haris Rozajac
Haris Rozajac
parent
95ea14cc72
commit
49739618fa
@@ -0,0 +1,79 @@
|
||||
package schemaversion
|
||||
|
||||
// migration_utils.go contains shared utility functions used across multiple schema version migrations.
|
||||
|
||||
// GetStringValue safely extracts a string value from a map, returning empty string if not found or not a string
|
||||
func GetStringValue(m map[string]interface{}, key string) string {
|
||||
if value, ok := m[key]; ok {
|
||||
if s, ok := value.(string); ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetBoolValue safely extracts a boolean value from a map, returning false if not found or not a boolean
|
||||
func GetBoolValue(m map[string]interface{}, key string) bool {
|
||||
if value, ok := m[key]; ok {
|
||||
if b, ok := value.(bool); ok {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetIntValue safely extracts an integer value from a map, returning defaultValue if not found or not convertible
|
||||
func GetIntValue(m map[string]interface{}, key string, defaultValue int) int {
|
||||
if value, ok := m[key]; ok {
|
||||
if i, ok := ConvertToInt(value); ok {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// GetFloatValue safely extracts a float value from a map, returning defaultValue if not found or not convertible
|
||||
func GetFloatValue(m map[string]interface{}, key string, defaultValue float64) float64 {
|
||||
if value, ok := m[key]; ok {
|
||||
if f, ok := ConvertToFloat(value); ok {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// ConvertToFloat converts various numeric types to float64
|
||||
func ConvertToFloat(value interface{}) (float64, bool) {
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
return v, true
|
||||
case int:
|
||||
return float64(v), true
|
||||
case int64:
|
||||
return float64(v), true
|
||||
case float32:
|
||||
return float64(v), true
|
||||
case int32:
|
||||
return float64(v), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToInt converts various numeric types to int
|
||||
func ConvertToInt(value interface{}) (int, bool) {
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
return v, true
|
||||
case float64:
|
||||
return int(v), true
|
||||
case int64:
|
||||
return int(v), true
|
||||
case float32:
|
||||
return int(v), true
|
||||
case int32:
|
||||
return int(v), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
MIN_VERSION = 16
|
||||
MIN_VERSION = 15
|
||||
LATEST_VERSION = 41
|
||||
)
|
||||
|
||||
@@ -38,6 +38,7 @@ type PanelPluginInfoProvider interface {
|
||||
|
||||
func GetMigrations(dsInfoProvider DataSourceInfoProvider, panelProvider PanelPluginInfoProvider) map[int]SchemaVersionMigrationFunc {
|
||||
return map[int]SchemaVersionMigrationFunc{
|
||||
16: V16,
|
||||
17: V17,
|
||||
18: V18,
|
||||
19: V19,
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
package schemaversion
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
gridColumnCount = 24.0
|
||||
defaultPanelSpan = 4.0
|
||||
defaultRowHeight = 250.0
|
||||
gridCellHeight = 30.0
|
||||
gridCellVMargin = 8.0
|
||||
minPanelHeight = gridCellHeight * 3.0
|
||||
panelHeightStep = gridCellHeight + gridCellVMargin
|
||||
)
|
||||
|
||||
// V16 migrates dashboard layout from the old row-based system to the modern grid-based layout.
|
||||
// This migration follows the exact logic from DashboardMigrator.ts to ensure consistency between frontend and backend.
|
||||
func V16(dashboard map[string]interface{}) error {
|
||||
dashboard["schemaVersion"] = 16
|
||||
|
||||
upgradeToGridLayout(dashboard)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgradeToGridLayout(dashboard map[string]interface{}) {
|
||||
rowsInterface, ok := dashboard["rows"]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
rows, ok := rowsInterface.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle empty rows
|
||||
if len(rows) == 0 {
|
||||
dashboard["panels"] = []interface{}{}
|
||||
delete(dashboard, "rows")
|
||||
return
|
||||
}
|
||||
|
||||
yPos := 0
|
||||
widthFactor := gridColumnCount / 12.0
|
||||
|
||||
// Find max panel ID (lines 1014-1021 in TS)
|
||||
maxPanelID := getMaxPanelID(rows)
|
||||
nextRowID := maxPanelID + 1
|
||||
|
||||
// Get existing panels
|
||||
var finalPanels []interface{}
|
||||
if existingPanels, ok := dashboard["panels"].([]interface{}); ok {
|
||||
finalPanels = existingPanels
|
||||
}
|
||||
|
||||
// Add special "row" panels if even one row is collapsed, repeated or has visible title (line 1028 in TS)
|
||||
showRows := shouldShowRows(rows)
|
||||
|
||||
// Process each row (line 1030 in TS)
|
||||
for _, rowInterface := range rows {
|
||||
row, ok := rowInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip repeated rows (line 1031-1033 in TS)
|
||||
if _, hasRepeatIteration := row["repeatIteration"]; hasRepeatIteration {
|
||||
continue
|
||||
}
|
||||
|
||||
height := getRowHeight(row)
|
||||
rowGridHeight := getGridHeight(height)
|
||||
isCollapsed := GetBoolValue(row, "collapse")
|
||||
|
||||
var rowPanel map[string]interface{}
|
||||
|
||||
// First pass: assign IDs to panels that don't have them
|
||||
panelsInRow, ok := row["panels"].([]interface{})
|
||||
if !ok {
|
||||
panelsInRow = []interface{}{}
|
||||
}
|
||||
|
||||
for _, panelInterface := range panelsInRow {
|
||||
panel, ok := panelInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// Assign ID if missing
|
||||
if _, hasID := panel["id"]; !hasID {
|
||||
panel["id"] = nextRowID
|
||||
nextRowID++
|
||||
}
|
||||
}
|
||||
|
||||
if showRows {
|
||||
// add special row panel (lines 1041-1058 in TS)
|
||||
rowPanel = map[string]interface{}{
|
||||
"id": nextRowID,
|
||||
"type": "row",
|
||||
"title": GetStringValue(row, "title"),
|
||||
"collapsed": isCollapsed,
|
||||
"repeat": GetStringValue(row, "repeat"),
|
||||
"panels": []interface{}{},
|
||||
"gridPos": map[string]interface{}{
|
||||
"x": 0,
|
||||
"y": yPos,
|
||||
"w": int(gridColumnCount),
|
||||
"h": rowGridHeight,
|
||||
},
|
||||
}
|
||||
nextRowID++
|
||||
yPos++
|
||||
}
|
||||
|
||||
rowArea := newRowArea(rowGridHeight, gridColumnCount, yPos)
|
||||
|
||||
// Process all panels in this row (lines 1062-1087 in TS)
|
||||
for _, panelInterface := range panelsInRow {
|
||||
panel, ok := panelInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Set default span (line 1063 in TS)
|
||||
span := GetFloatValue(panel, "span", defaultPanelSpan)
|
||||
|
||||
// Handle minSpan conversion (lines 1064-1066 in TS)
|
||||
if minSpan, hasMinSpan := panel["minSpan"]; hasMinSpan {
|
||||
if minSpanFloat, ok := ConvertToFloat(minSpan); ok && minSpanFloat > 0 {
|
||||
panel["minSpan"] = int(math.Min(float64(gridColumnCount), (float64(gridColumnCount)/12.0)*minSpanFloat))
|
||||
}
|
||||
}
|
||||
|
||||
panelWidth := int(math.Floor(span * widthFactor))
|
||||
panelHeight := rowGridHeight
|
||||
if panelHeightValue, hasHeight := panel["height"]; hasHeight {
|
||||
if h, ok := ConvertToFloat(panelHeightValue); ok {
|
||||
panelHeight = getGridHeight(h)
|
||||
}
|
||||
}
|
||||
|
||||
panelPos := rowArea.getPanelPosition(panelHeight, panelWidth)
|
||||
yPos = rowArea.yPos
|
||||
|
||||
// Set gridPos (lines 1072-1077 in TS)
|
||||
panel["gridPos"] = map[string]interface{}{
|
||||
"x": GetIntValue(panelPos, "x", 0),
|
||||
"y": yPos + GetIntValue(panelPos, "y", 0),
|
||||
"w": panelWidth,
|
||||
"h": panelHeight,
|
||||
}
|
||||
rowArea.addPanel(panel["gridPos"].(map[string]interface{}))
|
||||
|
||||
// Remove span (line 1080 in TS)
|
||||
delete(panel, "span")
|
||||
|
||||
// Exact logic from lines 1082-1086 in TS
|
||||
if rowPanel != nil && isCollapsed {
|
||||
// Add to collapsed row's nested panels
|
||||
if rowPanelPanels, ok := rowPanel["panels"].([]interface{}); ok {
|
||||
rowPanel["panels"] = append(rowPanelPanels, panel)
|
||||
}
|
||||
} else {
|
||||
// Add directly to dashboard panels
|
||||
finalPanels = append(finalPanels, panel)
|
||||
}
|
||||
}
|
||||
|
||||
// Add row panel after processing all panels (lines 1089-1091 in TS)
|
||||
if rowPanel != nil {
|
||||
finalPanels = append(finalPanels, rowPanel)
|
||||
}
|
||||
|
||||
// Update yPos (lines 1093-1095 in TS)
|
||||
if rowPanel == nil || !isCollapsed {
|
||||
yPos += rowGridHeight
|
||||
}
|
||||
}
|
||||
|
||||
// Update the dashboard
|
||||
dashboard["panels"] = finalPanels
|
||||
delete(dashboard, "rows")
|
||||
}
|
||||
|
||||
// rowArea represents dashboard row filled by panels
|
||||
type rowArea struct {
|
||||
area []int
|
||||
yPos int
|
||||
height int
|
||||
}
|
||||
|
||||
func newRowArea(height int, width int, rowYPos int) *rowArea {
|
||||
area := make([]int, width)
|
||||
return &rowArea{
|
||||
area: area,
|
||||
yPos: rowYPos,
|
||||
height: height,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rowArea) reset() {
|
||||
for i := range r.area {
|
||||
r.area[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rowArea) addPanel(gridPos map[string]interface{}) {
|
||||
x := GetIntValue(gridPos, "x", 0)
|
||||
y := GetIntValue(gridPos, "y", 0)
|
||||
w := GetIntValue(gridPos, "w", 0)
|
||||
h := GetIntValue(gridPos, "h", 0)
|
||||
|
||||
for i := x; i < x+w && i < len(r.area); i++ {
|
||||
newHeight := y + h - r.yPos
|
||||
if newHeight > r.area[i] {
|
||||
r.area[i] = newHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rowArea) getPanelPosition(panelHeight int, panelWidth int) map[string]interface{} {
|
||||
var startPlace, endPlace int
|
||||
found := false
|
||||
|
||||
// Find available space from right to left
|
||||
for i := len(r.area) - 1; i >= 0; i-- {
|
||||
if r.height-r.area[i] > 0 {
|
||||
if !found {
|
||||
endPlace = i
|
||||
found = true
|
||||
} else {
|
||||
if i < len(r.area)-1 && r.area[i] <= r.area[i+1] {
|
||||
startPlace = i
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found && endPlace-startPlace >= panelWidth-1 {
|
||||
// Find max height in the range
|
||||
yPos := 0
|
||||
for i := startPlace; i <= endPlace && i < len(r.area); i++ {
|
||||
if r.area[i] > yPos {
|
||||
yPos = r.area[i]
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"x": startPlace,
|
||||
"y": yPos,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap to next row
|
||||
r.yPos += r.height
|
||||
r.reset()
|
||||
return r.getPanelPosition(panelHeight, panelWidth)
|
||||
}
|
||||
|
||||
func getMaxPanelID(rows []interface{}) int {
|
||||
maxID := 0
|
||||
for _, rowInterface := range rows {
|
||||
if row, ok := rowInterface.(map[string]interface{}); ok {
|
||||
if panels, ok := row["panels"].([]interface{}); ok {
|
||||
for _, panelInterface := range panels {
|
||||
if panel, ok := panelInterface.(map[string]interface{}); ok {
|
||||
if id := GetIntValue(panel, "id", 0); id > maxID {
|
||||
maxID = id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxID
|
||||
}
|
||||
|
||||
func shouldShowRows(rows []interface{}) bool {
|
||||
for _, rowInterface := range rows {
|
||||
if row, ok := rowInterface.(map[string]interface{}); ok {
|
||||
if GetBoolValue(row, "collapse") || GetBoolValue(row, "showTitle") || GetStringValue(row, "repeat") != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getRowHeight(row map[string]interface{}) float64 {
|
||||
if height, ok := row["height"]; ok {
|
||||
if h, ok := ConvertToFloat(height); ok {
|
||||
return h
|
||||
}
|
||||
}
|
||||
return defaultRowHeight
|
||||
}
|
||||
|
||||
func getGridHeight(height float64) int {
|
||||
if height < minPanelHeight {
|
||||
height = minPanelHeight
|
||||
}
|
||||
return int(math.Ceil(height / panelHeightStep))
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -63,16 +63,9 @@ func migrateMinSpanToMaxPerRow(panel map[string]interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// Convert minSpan to a number (could be int, float64, etc.)
|
||||
var minSpan float64
|
||||
switch v := minSpanValue.(type) {
|
||||
case int:
|
||||
minSpan = float64(v)
|
||||
case float64:
|
||||
minSpan = v
|
||||
case int64:
|
||||
minSpan = float64(v)
|
||||
default:
|
||||
// Convert minSpan to a number using shared utility
|
||||
minSpan, ok := ConvertToFloat(minSpanValue)
|
||||
if !ok {
|
||||
// If we can't convert minSpan to a number, just delete it and return
|
||||
delete(panel, "minSpan")
|
||||
return
|
||||
|
||||
@@ -84,8 +84,8 @@ func upgradePanelLink(link map[string]interface{}) map[string]interface{} {
|
||||
|
||||
result := map[string]interface{}{
|
||||
"url": url,
|
||||
"title": getStringValue(link, "title"),
|
||||
"targetBlank": getBoolValue(link, "targetBlank"),
|
||||
"title": GetStringValue(link, "title"),
|
||||
"targetBlank": GetBoolValue(link, "targetBlank"),
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -111,11 +111,11 @@ func buildPanelLinkURL(link map[string]interface{}) string {
|
||||
// Add query parameters
|
||||
params := []string{}
|
||||
|
||||
if getBoolValue(link, "keepTime") {
|
||||
if GetBoolValue(link, "keepTime") {
|
||||
params = append(params, "$__url_time_range")
|
||||
}
|
||||
|
||||
if getBoolValue(link, "includeVars") {
|
||||
if GetBoolValue(link, "includeVars") {
|
||||
params = append(params, "$__all_variables")
|
||||
}
|
||||
|
||||
@@ -150,17 +150,3 @@ func slugifyForURL(name string) string {
|
||||
name = reSpaces.ReplaceAllString(name, "-")
|
||||
return name
|
||||
}
|
||||
|
||||
func getStringValue(m map[string]interface{}, key string) string {
|
||||
if v, ok := m[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getBoolValue(m map[string]interface{}, key string) bool {
|
||||
if v, ok := m[key].(bool); ok {
|
||||
return v
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func removeRepeatedPanels(panels []interface{}) []interface{} {
|
||||
// Filter out repeats in collapsed rows
|
||||
if p["type"] == "row" {
|
||||
if nestedPanels, ok := p["panels"].([]interface{}); ok {
|
||||
var filteredNestedPanels []interface{}
|
||||
filteredNestedPanels := []interface{}{}
|
||||
for _, nestedPanel := range nestedPanels {
|
||||
if np, ok := nestedPanel.(map[string]interface{}); ok {
|
||||
if _, hasRepeatPanelId := np["repeatPanelId"]; !hasRepeatPanelId {
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"title": "V16 Grid Layout Migration Test Dashboard",
|
||||
"schemaVersion": 15,
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"height": 250,
|
||||
"panels": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "graph",
|
||||
"title": "CPU Usage",
|
||||
"span": 6
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "singlestat",
|
||||
"title": "Memory Usage",
|
||||
"span": 6
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapse": true,
|
||||
"height": 300,
|
||||
"title": "Collapsed Row",
|
||||
"panels": [
|
||||
{
|
||||
"id": 3,
|
||||
"type": "table",
|
||||
"title": "Process List",
|
||||
"span": 12
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "graph",
|
||||
"title": "Network I/O",
|
||||
"span": 6,
|
||||
"height": 200
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "graph",
|
||||
"title": "Disk I/O",
|
||||
"span": 6,
|
||||
"height": 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"showTitle": true,
|
||||
"title": "Visible Row Title",
|
||||
"height": 200,
|
||||
"panels": [
|
||||
{
|
||||
"id": 6,
|
||||
"type": "gauge",
|
||||
"title": "Temperature",
|
||||
"span": 4,
|
||||
"minSpan": 2
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "stat",
|
||||
"title": "Uptime",
|
||||
"span": 4
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "bargauge",
|
||||
"title": "Load Average",
|
||||
"span": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"height": 150,
|
||||
"panels": [
|
||||
{
|
||||
"id": 9,
|
||||
"type": "text",
|
||||
"title": "Description Panel",
|
||||
"span": 8
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "logs",
|
||||
"title": "System Logs",
|
||||
"span": 4,
|
||||
"height": 100
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"repeat": "server",
|
||||
"height": 250,
|
||||
"panels": [
|
||||
{
|
||||
"id": 11,
|
||||
"type": "graph",
|
||||
"title": "Server Metrics",
|
||||
"span": 12
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"repeatIteration": true,
|
||||
"height": 250,
|
||||
"panels": [
|
||||
{
|
||||
"id": 12,
|
||||
"type": "graph",
|
||||
"title": "Should be skipped",
|
||||
"span": 12
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
{
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"id": 1,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "CPU Usage",
|
||||
"type": "graph"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 1
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"colorMode": "none",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"orientation": "horizontal",
|
||||
"percentChangeColorMode": "standard",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"mean"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showPercentChange": false,
|
||||
"textMode": "auto",
|
||||
"wideLayout": true
|
||||
},
|
||||
"pluginVersion": "1.0.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Memory Usage",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 13,
|
||||
"panels": [],
|
||||
"repeat": "",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"collapsed": true,
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 14,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 9
|
||||
},
|
||||
"id": 3,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Process List",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 17
|
||||
},
|
||||
"height": 200,
|
||||
"id": 4,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Network I/O",
|
||||
"type": "graph"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 17
|
||||
},
|
||||
"height": 200,
|
||||
"id": 5,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Disk I/O",
|
||||
"type": "graph"
|
||||
}
|
||||
],
|
||||
"repeat": "",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Collapsed Row",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 18
|
||||
},
|
||||
"id": 6,
|
||||
"maxPerRow": 6,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Temperature",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 18
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"justifyMode": "auto",
|
||||
"percentChangeColorMode": "standard",
|
||||
"showPercentChange": false,
|
||||
"textMode": "auto",
|
||||
"wideLayout": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Uptime",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 18
|
||||
},
|
||||
"id": 8,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Load Average",
|
||||
"type": "bargauge"
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 17
|
||||
},
|
||||
"id": 15,
|
||||
"panels": [],
|
||||
"repeat": "",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Visible Row Title",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 16,
|
||||
"x": 0,
|
||||
"y": 25
|
||||
},
|
||||
"id": 9,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Description Panel",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 25
|
||||
},
|
||||
"height": 100,
|
||||
"id": 10,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "System Logs",
|
||||
"type": "logs"
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 24
|
||||
},
|
||||
"id": 16,
|
||||
"panels": [],
|
||||
"repeat": "",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 30
|
||||
},
|
||||
"id": 11,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Server Metrics",
|
||||
"type": "graph"
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 29
|
||||
},
|
||||
"id": 17,
|
||||
"panels": [],
|
||||
"repeat": "server",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "",
|
||||
"type": "row"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 41,
|
||||
"title": "V16 Grid Layout Migration Test Dashboard"
|
||||
}
|
||||
Reference in New Issue
Block a user