diff --git a/common-lib/CONTRIB.md b/common-lib/CONTRIB.md new file mode 100644 index 000000000..fb85608dd --- /dev/null +++ b/common-lib/CONTRIB.md @@ -0,0 +1,20 @@ + +## Panels overview + +All panels in this lib should implement one of the following methods: + +- `panel.new(title,targets,description)` - creates new panel. List of arguments could vary; +- `panel.stylize(allLayers=true)` - directly applies this panel style to existing panel. By default includes all layers of styles. To apply only top layer, set allLayers=false. This mode is useful to cherry-pick style layers to create new style combination. + +Some other methods could be found such as: +- `panel.stylizeByRegexp(regexp)` - attaches style as panel overrides (by regexp); +- `panel.stylizeByName(name)` - attaches style as panel overrides (by name). + +## Panels common groups + +This library consists of multiple common groups of panels for widely used resources such as CPU, memory, disks and so on. + +All of those groups inherit `generic` group as their base. + +All panels inherit `generic/base.libsonnet` via `generic//base.libsonnet`. + diff --git a/common-lib/Makefile b/common-lib/Makefile new file mode 100644 index 000000000..46933e1a4 --- /dev/null +++ b/common-lib/Makefile @@ -0,0 +1,23 @@ +JSONNET_FMT := jsonnetfmt -n 2 --max-blank-lines 1 --string-style s --comment-style s + +.PHONY: all +all: build + +vendor: jsonnetfile.json + jb install + +.PHONY: build +build: vendor + +.PHONY: fmt +fmt: + find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \ + xargs -n 1 -- $(JSONNET_FMT) -i + +.PHONY: lint +lint: build + find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \ + while read f; do \ + $(JSONNET_FMT) "$$f" | diff -u "$$f" -; \ + done + diff --git a/common-lib/README.md b/common-lib/README.md new file mode 100644 index 000000000..93b356324 --- /dev/null +++ b/common-lib/README.md @@ -0,0 +1,33 @@ +# Grafana integrations common lib + +This common library can be used to quickly create dashboards' `panels` and `annotations`. + +By using this common library we can 'enforce' common style choices across multiple dashboards and mixins. + +## Import + +```sh +jb init +jb install https://github.com/grafana/jsonnet-libs/common-lib +``` + +## Use + +### Create new panel + +```jsonnet + +local commonlib = import 'github.com/grafana/jsonnet-libs/common-lib/common/main.libsonnet'; +local cpuUsage = commonlib.panels.cpu.timeSeries.utilization.new(targets=[targets.cpuUsage]); + +``` + +### Mutate exisiting panel with style options + +```jsonnet + +local commonlib = import 'github.com/grafana/jsonnet-libs/common-lib/common/main.libsonnet'; +local cpuPanel = oldPanel + commonlib.panels.cpu.timeSeries.utilization.stylize(); +``` + +See [windows-observ-lib](./windows-observ-lib) for full example. diff --git a/common-lib/common/annotations/base.libsonnet b/common-lib/common/annotations/base.libsonnet new file mode 100644 index 000000000..8cb149be7 --- /dev/null +++ b/common-lib/common/annotations/base.libsonnet @@ -0,0 +1,32 @@ +local g = import '../g.libsonnet'; +local annotation = g.dashboard.annotation; +{ + new( + title, + target, + ): + annotation.withEnable(true) + + annotation.withName(title) + + annotation.withDatasourceMixin(target.datasource) + { + titleFormat: title, + expr: target.expr, + + } + + (if std.objectHas(target, 'interval') then { step: target.interval } else {}), + + withTagKeys(value): + { + tagKeys: value, + }, + withValueForTime(value=false): + { + useValueForTime: value, + }, + withTextFormat(value=''): + { + textFormat: value, + }, + + +} diff --git a/common-lib/common/annotations/fatal.libsonnet b/common-lib/common/annotations/fatal.libsonnet new file mode 100644 index 000000000..409d62d25 --- /dev/null +++ b/common-lib/common/annotations/fatal.libsonnet @@ -0,0 +1,14 @@ +local g = import '../g.libsonnet'; +local annotation = g.dashboard.annotation; +local base = import './base.libsonnet'; + +// Show fatal or critical events as annotations +base { + new( + title, + target, + ): + super.new(title, target) + + annotation.withIconColor('light-purple') + + annotation.withHide(true), +} diff --git a/common-lib/common/annotations/main.libsonnet b/common-lib/common/annotations/main.libsonnet new file mode 100644 index 000000000..a904f9f3a --- /dev/null +++ b/common-lib/common/annotations/main.libsonnet @@ -0,0 +1,6 @@ +{ + base: import './base.libsonnet', + reboot: import './reboot.libsonnet', + serviceFailed: import './service_failed.libsonnet', + fatal: import './fatal.libsonnet', +} diff --git a/common-lib/common/annotations/reboot.libsonnet b/common-lib/common/annotations/reboot.libsonnet new file mode 100644 index 000000000..a6d67e111 --- /dev/null +++ b/common-lib/common/annotations/reboot.libsonnet @@ -0,0 +1,16 @@ +local g = import '../g.libsonnet'; +local annotation = g.dashboard.annotation; +local base = import './base.libsonnet'; + +base { + new( + title, + target, + instanceLabels, + ): + super.new(title, target) + + annotation.withIconColor('light-yellow') + + annotation.withHide(true) + + { useValueForTime: 'on' } + + base.withTagKeys(instanceLabels), +} diff --git a/common-lib/common/annotations/service_failed.libsonnet b/common-lib/common/annotations/service_failed.libsonnet new file mode 100644 index 000000000..63f601f05 --- /dev/null +++ b/common-lib/common/annotations/service_failed.libsonnet @@ -0,0 +1,13 @@ +local g = import '../g.libsonnet'; +local annotation = g.dashboard.annotation; +local base = import './base.libsonnet'; + +base { + new( + title, + target, + ): + super.new(title, target) + + annotation.withIconColor('light-orange') + + annotation.withHide(true), +} diff --git a/common-lib/common/g.libsonnet b/common-lib/common/g.libsonnet new file mode 100644 index 000000000..6da9f4eef --- /dev/null +++ b/common-lib/common/g.libsonnet @@ -0,0 +1 @@ +import 'github.com/grafana/grafonnet/gen/grafonnet-v10.0.0/main.libsonnet' diff --git a/common-lib/common/main.libsonnet b/common-lib/common/main.libsonnet new file mode 100644 index 000000000..4b7c76e3b --- /dev/null +++ b/common-lib/common/main.libsonnet @@ -0,0 +1,5 @@ +{ + annotations: import './annotations/main.libsonnet', + panels: import './panels.libsonnet', + utils: import './utils.libsonnet', +} diff --git a/common-lib/common/panels.libsonnet b/common-lib/common/panels.libsonnet new file mode 100644 index 000000000..7674ad570 --- /dev/null +++ b/common-lib/common/panels.libsonnet @@ -0,0 +1,31 @@ +local g = import './g.libsonnet'; + +{ + generic: { + stat: import './panels/generic/stat/main.libsonnet', + timeSeries: import './panels/generic/timeSeries/main.libsonnet', + table: import './panels/generic/table/main.libsonnet', + statusHistory: import './panels/generic/statusHistory/main.libsonnet', + }, + network: { + timeSeries: import './panels/network/timeSeries/main.libsonnet', + }, + system: { + stat: import './panels/system/stat/main.libsonnet', + table: import './panels/system/table/main.libsonnet', + statusHistory: import './panels/system/statusHistory/main.libsonnet', + }, + cpu: { + stat: import './panels/cpu/stat/main.libsonnet', + timeSeries: import './panels/cpu/timeSeries/main.libsonnet', + }, + memory: { + stat: import './panels/memory/stat/main.libsonnet', + timeSeries: import './panels/memory/timeSeries/main.libsonnet', + }, + disk: { + timeSeries: import './panels/disk/timeSeries/main.libsonnet', + table: import './panels/disk/table/main.libsonnet', + stat: import './panels/disk/stat/main.libsonnet', + }, +} diff --git a/common-lib/common/panels/cpu/stat/base.libsonnet b/common-lib/common/panels/cpu/stat/base.libsonnet new file mode 100644 index 000000000..587b02b91 --- /dev/null +++ b/common-lib/common/panels/cpu/stat/base.libsonnet @@ -0,0 +1,7 @@ +local g = import '../../../g.libsonnet'; +local stat = g.panel.stat; +local base = import '../../generic/stat/base.libsonnet'; + +base { + +} diff --git a/common-lib/common/panels/cpu/stat/count.libsonnet b/common-lib/common/panels/cpu/stat/count.libsonnet new file mode 100644 index 000000000..915060b49 --- /dev/null +++ b/common-lib/common/panels/cpu/stat/count.libsonnet @@ -0,0 +1,21 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/stat/main.libsonnet'; +local base = import './base.libsonnet'; +local stat = g.panel.stat; + +base { + new( + title='CPU count', + targets, + description=||| + CPU count is the number of processor cores or central processing units (CPUs) in a computer, + determining its processing capability and ability to handle tasks concurrently. + ||| + ): + super.new(title, targets, description), + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + generic.info.stylize(allLayers=false), + +} diff --git a/common-lib/common/panels/cpu/stat/main.libsonnet b/common-lib/common/panels/cpu/stat/main.libsonnet new file mode 100644 index 000000000..df20b658e --- /dev/null +++ b/common-lib/common/panels/cpu/stat/main.libsonnet @@ -0,0 +1,5 @@ +{ + base: import './base.libsonnet', + usage: import './usage.libsonnet', + count: import './count.libsonnet', +} diff --git a/common-lib/common/panels/cpu/stat/usage.libsonnet b/common-lib/common/panels/cpu/stat/usage.libsonnet new file mode 100644 index 000000000..bf019157d --- /dev/null +++ b/common-lib/common/panels/cpu/stat/usage.libsonnet @@ -0,0 +1,22 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/stat/main.libsonnet'; +local base = import './base.libsonnet'; +local stat = g.panel.stat; + +base { + new( + title='CPU usage', + targets, + description=||| + Total CPU utilization percent is a metric that indicates the overall level of central processing unit (CPU) usage in a computer system. + It represents the combined load placed on all CPU cores or processors. + + For instance, if the total CPU utilization percent is 50%, it means that, + on average, half of the CPU's processing capacity is being used to execute tasks. A higher percentage indicates that the CPU is working more intensively, potentially leading to system slowdowns if it remains consistently high. + ||| + ): + super.new(title, targets, description), + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + generic.percentage.stylize(allLayers=false), +} diff --git a/common-lib/common/panels/cpu/timeSeries/base.libsonnet b/common-lib/common/panels/cpu/timeSeries/base.libsonnet new file mode 100644 index 000000000..622d9bed7 --- /dev/null +++ b/common-lib/common/panels/cpu/timeSeries/base.libsonnet @@ -0,0 +1,6 @@ +local g = import '../../../g.libsonnet'; +local base = import '../../generic/timeSeries/base.libsonnet'; + +base { + +} diff --git a/common-lib/common/panels/cpu/timeSeries/main.libsonnet b/common-lib/common/panels/cpu/timeSeries/main.libsonnet new file mode 100644 index 000000000..3e9006578 --- /dev/null +++ b/common-lib/common/panels/cpu/timeSeries/main.libsonnet @@ -0,0 +1,5 @@ +{ + base: import './base.libsonnet', + utilization: import './utilization.libsonnet', + utilizationByMode: import './utilization_by_mode.libsonnet', +} diff --git a/common-lib/common/panels/cpu/timeSeries/utilization.libsonnet b/common-lib/common/panels/cpu/timeSeries/utilization.libsonnet new file mode 100644 index 000000000..fd365ac09 --- /dev/null +++ b/common-lib/common/panels/cpu/timeSeries/utilization.libsonnet @@ -0,0 +1,22 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/timeSeries/main.libsonnet'; +local base = import './base.libsonnet'; +base { + new( + title='CPU usage', + targets, + description=||| + Total CPU utilization percent is a metric that indicates the overall level of central processing unit (CPU) usage in a computer system. + It represents the combined load placed on all CPU cores or processors. + + For instance, if the total CPU utilization percent is 50%, it means that, + on average, half of the CPU's processing capacity is being used to execute tasks. A higher percentage indicates that the CPU is working more intensively, potentially leading to system slowdowns if it remains consistently high. + ||| + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + generic.percentage.stylize(allLayers=false), +} diff --git a/common-lib/common/panels/cpu/timeSeries/utilization_by_mode.libsonnet b/common-lib/common/panels/cpu/timeSeries/utilization_by_mode.libsonnet new file mode 100644 index 000000000..177c6ea49 --- /dev/null +++ b/common-lib/common/panels/cpu/timeSeries/utilization_by_mode.libsonnet @@ -0,0 +1,45 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +base { + new( + title='CPU usage by modes', + targets, + description='CPU usage by different modes.' + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + local timeSeries = g.panel.timeSeries; + local fieldOverride = g.panel.timeSeries.fieldOverride; + + (if allLayers then super.stylize() else {}) + + + timeSeries.standardOptions.withUnit('percent') + + timeSeries.fieldConfig.defaults.custom.withFillOpacity(80) + + timeSeries.fieldConfig.defaults.custom.withStacking({ mode: 'normal' }) + + timeSeries.standardOptions.withOverrides( + [ + fieldOverride.byName.new('idle') + + fieldOverride.byName.withPropertiesFromOptions( + timeSeries.standardOptions.color.withMode('fixed') + + timeSeries.standardOptions.color.withFixedColor('light-blue'), + ), + fieldOverride.byName.new('interrupt') + + fieldOverride.byName.withPropertiesFromOptions( + timeSeries.standardOptions.color.withMode('fixed') + + timeSeries.standardOptions.color.withFixedColor('light-purple'), + ), + fieldOverride.byName.new('user') + + fieldOverride.byName.withPropertiesFromOptions( + timeSeries.standardOptions.color.withMode('fixed') + + timeSeries.standardOptions.color.withFixedColor('light-orange'), + ), + fieldOverride.byRegexp.new('system|privileged') + + fieldOverride.byRegexp.withPropertiesFromOptions( + timeSeries.standardOptions.color.withMode('fixed') + + timeSeries.standardOptions.color.withFixedColor('light-red'), + ), + ] + ), +} diff --git a/common-lib/common/panels/disk/stat/base.libsonnet b/common-lib/common/panels/disk/stat/base.libsonnet new file mode 100644 index 000000000..09aef26e7 --- /dev/null +++ b/common-lib/common/panels/disk/stat/base.libsonnet @@ -0,0 +1,6 @@ +local g = import '../../../g.libsonnet'; +local stat = g.panel.stat; +local base = import '../../generic/stat/base.libsonnet'; + +base { +} diff --git a/common-lib/common/panels/disk/stat/main.libsonnet b/common-lib/common/panels/disk/stat/main.libsonnet new file mode 100644 index 000000000..79f34433d --- /dev/null +++ b/common-lib/common/panels/disk/stat/main.libsonnet @@ -0,0 +1,4 @@ +{ + base: import './base.libsonnet', + total: import './total.libsonnet', +} diff --git a/common-lib/common/panels/disk/stat/total.libsonnet b/common-lib/common/panels/disk/stat/total.libsonnet new file mode 100644 index 000000000..6e81dfc05 --- /dev/null +++ b/common-lib/common/panels/disk/stat/total.libsonnet @@ -0,0 +1,20 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/stat/main.libsonnet'; +local base = import './base.libsonnet'; +local stat = g.panel.stat; + +base { + new( + title, + targets, + description='' + ): + super.new(title=title, targets=targets, description=description), + + stylize(allLayers=true): + + (if allLayers then super.stylize() else {}) + + + generic.info.stylize(allLayers=false) + + stat.standardOptions.withUnit('bytes'), +} diff --git a/common-lib/common/panels/disk/table/base.libsonnet b/common-lib/common/panels/disk/table/base.libsonnet new file mode 100644 index 000000000..6caa7f945 --- /dev/null +++ b/common-lib/common/panels/disk/table/base.libsonnet @@ -0,0 +1,10 @@ +local g = import '../../../g.libsonnet'; +local base = import '../../generic/table/base.libsonnet'; +local table = g.panel.table; +local fieldOverride = g.panel.table.fieldOverride; +local custom = table.fieldConfig.defaults.custom; +local defaults = table.fieldConfig.defaults; +local options = table.options; +base { + +} diff --git a/common-lib/common/panels/disk/table/main.libsonnet b/common-lib/common/panels/disk/table/main.libsonnet new file mode 100644 index 000000000..5493c2761 --- /dev/null +++ b/common-lib/common/panels/disk/table/main.libsonnet @@ -0,0 +1,4 @@ +{ + base: import './base.libsonnet', + usage: import './usage.libsonnet', +} diff --git a/common-lib/common/panels/disk/table/usage.libsonnet b/common-lib/common/panels/disk/table/usage.libsonnet new file mode 100644 index 000000000..17b7db7eb --- /dev/null +++ b/common-lib/common/panels/disk/table/usage.libsonnet @@ -0,0 +1,142 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local table = g.panel.table; +local fieldOverride = g.panel.table.fieldOverride; +local custom = table.fieldConfig.defaults.custom; +local defaults = table.fieldConfig.defaults; +local options = table.options; +base { + new( + title='Disk space usage', + totalTarget, + usageTarget, + groupLabel, + description=||| + This table provides information about total disk space, used space, available space, and usage percentages for each mounted file system on the system. + |||, + ): + // validate inputs + std.prune( + { + checks: [ + if !(std.objectHas(totalTarget, 'format') && std.assertEqual(totalTarget.format, 'table')) then error 'totalTarget format must be "table"', + if !(std.objectHas(totalTarget, 'instant') && std.assertEqual(totalTarget.instant, true)) then error 'totalTarget must be type instant', + if !(std.objectHas(usageTarget, 'format') && std.assertEqual(usageTarget.format, 'table')) then error 'usageTarget format must be "table"', + if !(std.objectHas(usageTarget, 'instant') && std.assertEqual(usageTarget.instant, true)) then error 'usageTarget must be type instant', + ], + } + ) + + super.new( + title=title, + targets=[ + totalTarget { refId: 'TOTAL' }, + usageTarget { refId: 'USAGE' }, + ], + description=description, + ) + + table.standardOptions.thresholds.withSteps( + [ + table.thresholdStep.withColor('light-blue') + + table.thresholdStep.withValue(null), + table.thresholdStep.withColor('light-yellow') + + table.thresholdStep.withValue(0.8), + table.thresholdStep.withColor('light-red') + + table.thresholdStep.withValue(0.9), + ] + ) + + + table.standardOptions.withOverrides([ + fieldOverride.byName.new('Mounted on') + + fieldOverride.byName.withProperty('custom.width', '260'), + fieldOverride.byName.new('Size') + + fieldOverride.byName.withProperty('custom.width', '80'), + fieldOverride.byName.new('Used') + + fieldOverride.byName.withProperty('custom.width', '80'), + fieldOverride.byName.new('Available') + + fieldOverride.byName.withProperty('custom.width', '80'), + fieldOverride.byName.new('Used, %') + + fieldOverride.byName.withProperty('custom.displayMode', 'basic') + + fieldOverride.byName.withPropertiesFromOptions( + table.standardOptions.withMax(1) + + table.standardOptions.withMin(0) + + table.standardOptions.withUnit('percentunit') + ), + ]) + + table.standardOptions.withUnit('bytes') + + table.queryOptions.withTransformationsMixin( + [ + { + id: 'groupBy', + options: { + fields: { + 'Value #TOTAL': { + aggregations: [ + 'lastNotNull', + ], + operation: 'aggregate', + }, + 'Value #USAGE': { + aggregations: [ + 'lastNotNull', + ], + operation: 'aggregate', + }, + [groupLabel]: { + aggregations: [], + operation: 'groupby', + }, + }, + }, + }, + { + id: 'merge', + options: {}, + }, + { + id: 'calculateField', + options: { + alias: 'Free', + binary: { + left: 'Value #TOTAL (lastNotNull)', + operator: '-', + reducer: 'sum', + right: 'Value #USAGE (lastNotNull)', + }, + mode: 'binary', + reduce: { + reducer: 'sum', + }, + }, + }, + { + id: 'calculateField', + options: { + alias: 'Used, %', + binary: { + left: 'Value #USAGE (lastNotNull)', + operator: '/', + reducer: 'sum', + right: 'Value #TOTAL (lastNotNull)', + }, + mode: 'binary', + reduce: { + reducer: 'sum', + }, + }, + }, + { + id: 'organize', + options: { + excludeByName: {}, + indexByName: {}, + renameByName: { + 'Value #TOTAL (lastNotNull)': 'Size', + 'Value #USAGE (lastNotNull)': 'Available', + [groupLabel]: 'Mounted on', + }, + }, + }, + self.transformations.sortBy('Mounted on'), + ] + ), +} diff --git a/common-lib/common/panels/disk/timeSeries/available.libsonnet b/common-lib/common/panels/disk/timeSeries/available.libsonnet new file mode 100644 index 000000000..0e707c022 --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/available.libsonnet @@ -0,0 +1,23 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Disk space available', + targets, + description='', + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + + timeSeries.standardOptions.withUnit('bytes') + + timeSeries.standardOptions.withMin(0), +} diff --git a/common-lib/common/panels/disk/timeSeries/base.libsonnet b/common-lib/common/panels/disk/timeSeries/base.libsonnet new file mode 100644 index 000000000..f5b09f21d --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/base.libsonnet @@ -0,0 +1,27 @@ +local g = import '../../../g.libsonnet'; +local base = import '../../generic/timeSeries/base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + // Decrease opacity (would look better with too many timeseries) + + defaults.custom.withFillOpacity(1), + + + withNegateOutPackets(regexp='/write|written/'): + defaults.custom.withAxisLabel('write(-) | read(+)') + + defaults.custom.withAxisCenteredZero(true) + + timeSeries.standardOptions.withOverrides( + fieldOverride.byRegexp.new(regexp) + + fieldOverride.byRegexp.withPropertiesFromOptions( + defaults.custom.withTransform('negative-Y') + ) + ), + +} diff --git a/common-lib/common/panels/disk/timeSeries/io_bytes_persec.libsonnet b/common-lib/common/panels/disk/timeSeries/io_bytes_persec.libsonnet new file mode 100644 index 000000000..addf4f495 --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/io_bytes_persec.libsonnet @@ -0,0 +1,31 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Disk reads/writes', + targets, + description='Disk read/writes in bytes per second.', + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + + timeSeries.standardOptions.withUnit('Bps') + // move 'IO busy time' to second axis if found + + timeSeries.standardOptions.withOverrides( + fieldOverride.byRegexp.new('/time|used|busy|util/') + + fieldOverride.byRegexp.withPropertiesFromOptions( + timeSeries.standardOptions.withUnit('percent') + + timeSeries.fieldConfig.defaults.custom.withDrawStyle('points') + + timeSeries.fieldConfig.defaults.custom.withAxisSoftMax(100) + ) + ), +} diff --git a/common-lib/common/panels/disk/timeSeries/io_queue.libsonnet b/common-lib/common/panels/disk/timeSeries/io_queue.libsonnet new file mode 100644 index 000000000..157796af6 --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/io_queue.libsonnet @@ -0,0 +1,23 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/timeSeries/main.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Disk IO queue', + targets, + description='Disk average IO queue.', + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + + self.withNegateOutPackets(), +} diff --git a/common-lib/common/panels/disk/timeSeries/io_wait_time.libsonnet b/common-lib/common/panels/disk/timeSeries/io_wait_time.libsonnet new file mode 100644 index 000000000..ae17d529e --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/io_wait_time.libsonnet @@ -0,0 +1,25 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Disk average wait time', + targets, + description=||| + The average time for requests issued to the device to be served. + This includes the time spent by the requests in queue and the time spent servicing them.' + ||| + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + timeSeries.standardOptions.withUnit('s') + + self.withNegateOutPackets(), +} diff --git a/common-lib/common/panels/disk/timeSeries/iops.libsonnet b/common-lib/common/panels/disk/timeSeries/iops.libsonnet new file mode 100644 index 000000000..fd30b051f --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/iops.libsonnet @@ -0,0 +1,23 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Disk I/O', + targets, + description=||| + The number of I/O requests per second for the device/volume. + ||| + ): + super.new(title, targets, description) + + self.stylize(), + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + timeSeries.standardOptions.withUnit('iops') + + self.withNegateOutPackets(), +} diff --git a/common-lib/common/panels/disk/timeSeries/main.libsonnet b/common-lib/common/panels/disk/timeSeries/main.libsonnet new file mode 100644 index 000000000..5fc8df51d --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/main.libsonnet @@ -0,0 +1,10 @@ +{ + base: import './base.libsonnet', + ioBytesPerSec: import './io_bytes_persec.libsonnet', + iops: import './iops.libsonnet', + ioQueue: import './io_queue.libsonnet', + ioWaitTime: import './io_wait_time.libsonnet', + available: import './available.libsonnet', + usage: import './usage.libsonnet', + usagePercent: import './usage_percent.libsonnet', +} diff --git a/common-lib/common/panels/disk/timeSeries/usage.libsonnet b/common-lib/common/panels/disk/timeSeries/usage.libsonnet new file mode 100644 index 000000000..84b2fab62 --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/usage.libsonnet @@ -0,0 +1,23 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Disk space used', + targets, + description=||| + Disk space usage is the amount of storage being used on a device's hard drive or storage medium, in bytes. + |||, + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + timeSeries.standardOptions.withUnit('bytes'), +} diff --git a/common-lib/common/panels/disk/timeSeries/usage_percent.libsonnet b/common-lib/common/panels/disk/timeSeries/usage_percent.libsonnet new file mode 100644 index 000000000..f5d355423 --- /dev/null +++ b/common-lib/common/panels/disk/timeSeries/usage_percent.libsonnet @@ -0,0 +1,25 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/timeSeries/main.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Disk space used, %', + targets, + description=||| + Disk space usage is the amount of storage being used on a device's hard drive or storage medium, in percent. + |||, + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + timeSeries.standardOptions.withUnit('percent') + + generic.percentage.stylize(allLayers=false), +} diff --git a/common-lib/common/panels/generic/base.libsonnet b/common-lib/common/panels/generic/base.libsonnet new file mode 100644 index 000000000..067cf534c --- /dev/null +++ b/common-lib/common/panels/generic/base.libsonnet @@ -0,0 +1,27 @@ +local g = import '../../g.libsonnet'; + +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; + +// This is base of ALL panels in the common lib +{ + new(targets, description=''): + // hidden field to hold styles modifiers + + timeSeries.queryOptions.withTargets(targets) + + timeSeries.panelOptions.withDescription(description) + // set first target's datasource + // to panel's datasource if only single type of + // datasoures are used accross all targets: + + (if std.length(std.set(targets, function(t) t.datasource.type)) == 1 then + timeSeries.queryOptions.withDatasource( + targets[0].datasource.type, targets[0].datasource.uid + ) else {}) + + self.stylize(), + + stylize(): {}, + +} diff --git a/common-lib/common/panels/generic/stat/base.libsonnet b/common-lib/common/panels/generic/stat/base.libsonnet new file mode 100644 index 000000000..8d70456ce --- /dev/null +++ b/common-lib/common/panels/generic/stat/base.libsonnet @@ -0,0 +1,12 @@ +local g = import '../../../g.libsonnet'; +local stat = g.panel.stat; +local base = import '../base.libsonnet'; + +base { + new(title, targets, description=''): + stat.new(title) + + super.new(targets, description), + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}), +} diff --git a/common-lib/common/panels/generic/stat/info.libsonnet b/common-lib/common/panels/generic/stat/info.libsonnet new file mode 100644 index 000000000..05fc5a7d7 --- /dev/null +++ b/common-lib/common/panels/generic/stat/info.libsonnet @@ -0,0 +1,21 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local stat = g.panel.stat; +// Simple info panel prototype with text or count of things. +base { + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + // Style choice: No color for simple text panels by default + + stat.options.withColorMode('fixed') + + stat.standardOptions.color.withFixedColor('text') + // Style choice: No graph + + stat.options.withGraphMode('none') + // Show last value by default, not mean. + + stat.options.withReduceOptions({}) + + stat.options.reduceOptions.withCalcsMixin( + [ + 'lastNotNull', + ] + ), +} diff --git a/common-lib/common/panels/generic/stat/main.libsonnet b/common-lib/common/panels/generic/stat/main.libsonnet new file mode 100644 index 000000000..93899e746 --- /dev/null +++ b/common-lib/common/panels/generic/stat/main.libsonnet @@ -0,0 +1,5 @@ +{ + base: import './base.libsonnet', + info: import './info.libsonnet', + percentage: import './percentage.libsonnet', +} diff --git a/common-lib/common/panels/generic/stat/percentage.libsonnet b/common-lib/common/panels/generic/stat/percentage.libsonnet new file mode 100644 index 000000000..a8a8d9097 --- /dev/null +++ b/common-lib/common/panels/generic/stat/percentage.libsonnet @@ -0,0 +1,24 @@ +local g = import '../../../g.libsonnet'; +local stat = g.panel.stat; +local base = import './base.libsonnet'; +// This panel can be used to display gauge metrics with possible values range 0-100%. +// Examples: cpu utilization, memory utilization etc. +base { + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + stat.standardOptions.withDecimals(1) + + stat.standardOptions.withUnit('percent') + + stat.options.withColorMode('value') + // Change color from blue(cold) to red(hot) + + stat.standardOptions.color.withMode('continuous-BlYlRd') + + stat.standardOptions.withMax(100) + + stat.standardOptions.withMin(0) + // Show last value by default, not mean. + + stat.options.withReduceOptions({}) + + stat.options.reduceOptions.withCalcsMixin( + [ + 'lastNotNull', + ] + ), +} diff --git a/common-lib/common/panels/generic/statusHistory/base.libsonnet b/common-lib/common/panels/generic/statusHistory/base.libsonnet new file mode 100644 index 000000000..bbc7b0292 --- /dev/null +++ b/common-lib/common/panels/generic/statusHistory/base.libsonnet @@ -0,0 +1,18 @@ +local g = import '../../../g.libsonnet'; +local base = import '../base.libsonnet'; +local statusHistory = g.panel.statusHistory; +local fieldOverride = g.panel.statusHistory.fieldOverride; +local custom = statusHistory.fieldConfig.defaults.custom; +local defaults = statusHistory.fieldConfig.defaults; +local options = statusHistory.options; +base { + + new(title, targets, description=''): + statusHistory.new(title) + + super.new(targets, description) + // Minimize number of points to avoid 'Too many data points' error on large time intervals + + statusHistory.queryOptions.withMaxDataPoints(50), + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}), +} diff --git a/common-lib/common/panels/generic/statusHistory/main.libsonnet b/common-lib/common/panels/generic/statusHistory/main.libsonnet new file mode 100644 index 000000000..3b77b583c --- /dev/null +++ b/common-lib/common/panels/generic/statusHistory/main.libsonnet @@ -0,0 +1,3 @@ +{ + base: import './base.libsonnet', +} diff --git a/common-lib/common/panels/generic/table/base.libsonnet b/common-lib/common/panels/generic/table/base.libsonnet new file mode 100644 index 000000000..1575b5020 --- /dev/null +++ b/common-lib/common/panels/generic/table/base.libsonnet @@ -0,0 +1,33 @@ +local g = import '../../../g.libsonnet'; + +local table = g.panel.table; +local fieldOverride = g.panel.table.fieldOverride; +local custom = table.fieldConfig.defaults.custom; +local defaults = table.fieldConfig.defaults; +local options = table.options; +local base = import '../base.libsonnet'; + +base { + new(title, targets, description=''): + table.new(title) + + super.new(targets, description), + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}), + + transformations+: { + sortBy(field, desc=false): + { + id: 'sortBy', + options: { + fields: {}, + sort: [ + { + field: field, + desc: desc, + }, + ], + }, + }, + }, +} diff --git a/common-lib/common/panels/generic/table/main.libsonnet b/common-lib/common/panels/generic/table/main.libsonnet new file mode 100644 index 000000000..3b77b583c --- /dev/null +++ b/common-lib/common/panels/generic/table/main.libsonnet @@ -0,0 +1,3 @@ +{ + base: import './base.libsonnet', +} diff --git a/common-lib/common/panels/generic/timeSeries/base.libsonnet b/common-lib/common/panels/generic/timeSeries/base.libsonnet new file mode 100644 index 000000000..6277d2b72 --- /dev/null +++ b/common-lib/common/panels/generic/timeSeries/base.libsonnet @@ -0,0 +1,33 @@ +local g = import '../../../g.libsonnet'; + +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +local base = import '../base.libsonnet'; +base { + new(title, targets, description=''): + timeSeries.new(title) + + super.new(targets, description), + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + // Style choice: Make lines more thick + + custom.withLineWidth(2) + // Style choice: Opacity level + + custom.withFillOpacity(30) + // Style choice: Don't show points on lines + + custom.withShowPoints('never') + // Style choice: Opacity gradient + + custom.withGradientMode('opacity') + // Style choice: Smoother lines + + custom.withLineInterpolation('smooth') + // Style choice: Show all values in tooltip, sorted + + options.tooltip.withMode('multi') + + options.tooltip.withSort('desc') + // Style choice: Use simple legend without any values (cleaner look) + + options.legend.withDisplayMode('list') + + options.legend.withCalcs([]), + +} diff --git a/common-lib/common/panels/generic/timeSeries/main.libsonnet b/common-lib/common/panels/generic/timeSeries/main.libsonnet new file mode 100644 index 000000000..92804486e --- /dev/null +++ b/common-lib/common/panels/generic/timeSeries/main.libsonnet @@ -0,0 +1,6 @@ +{ + base: import './base.libsonnet', + percentage: import './percentage.libsonnet', + threshold: import './threshold.libsonnet', + topkPercentage: import './topk_percentage.libsonnet', +} diff --git a/common-lib/common/panels/generic/timeSeries/percentage.libsonnet b/common-lib/common/panels/generic/timeSeries/percentage.libsonnet new file mode 100644 index 000000000..3a538bef6 --- /dev/null +++ b/common-lib/common/panels/generic/timeSeries/percentage.libsonnet @@ -0,0 +1,16 @@ +local g = import '../../../g.libsonnet'; +local timeSeries = g.panel.timeSeries; +local base = import './base.libsonnet'; +// This panel can be used to display gauge metrics with possible values range 0-100%. +// Examples: cpu utilization, memory utilization etc. +base { + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + timeSeries.standardOptions.withDecimals(1) + + timeSeries.standardOptions.withUnit('percent') + // Change color from blue(cold) to red(hot) + + timeSeries.standardOptions.color.withMode('continuous-BlYlRd') + + timeSeries.fieldConfig.defaults.custom.withGradientMode('scheme') + + timeSeries.standardOptions.withMax(100) + + timeSeries.standardOptions.withMin(0), +} diff --git a/common-lib/common/panels/generic/timeSeries/threshold.libsonnet b/common-lib/common/panels/generic/timeSeries/threshold.libsonnet new file mode 100644 index 000000000..291496549 --- /dev/null +++ b/common-lib/common/panels/generic/timeSeries/threshold.libsonnet @@ -0,0 +1,25 @@ +local g = import '../../../g.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local fieldConfig = g.panel.timeSeries.fieldConfig; + +// Turns any series to threshold line: dashed red line without gradient fill +{ + local this = self, + + stylize(): + fieldConfig.defaults.custom.withLineStyleMixin( + { + fill: 'dash', + dash: [10, 10], + } + ) + + fieldConfig.defaults.custom.withFillOpacity(0) + + timeSeries.standardOptions.color.withMode('fixed') + + timeSeries.standardOptions.color.withFixedColor('light-orange'), + stylizeByRegexp(regexp): + timeSeries.standardOptions.withOverrides( + fieldOverride.byRegexp.new(regexp) + + fieldOverride.byRegexp.withPropertiesFromOptions(this.stylize()) + ), +} diff --git a/common-lib/common/panels/generic/timeSeries/topk_percentage.libsonnet b/common-lib/common/panels/generic/timeSeries/topk_percentage.libsonnet new file mode 100644 index 000000000..cbd0edb85 --- /dev/null +++ b/common-lib/common/panels/generic/timeSeries/topk_percentage.libsonnet @@ -0,0 +1,63 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local generic = import './main.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local fieldConfig = g.panel.timeSeries.fieldConfig; +local standardOptions = g.panel.timeSeries.standardOptions; +// Style to display Top K metrics that can go from 0 to 100%. +// It constructs mean baseline automatically. +base { + new( + title, + target, + topk=25, + instanceLabels, + drillDownDashboardUid, + description='Top %s' % topk + ): + + local topTarget = target + { expr: 'topk(' + topk + ',' + target.expr + ')' } + + g.query.prometheus.withLegendFormat( + std.join(': ', std.map(function(l) '{{' + l + '}}', instanceLabels)) + ); + local meanTarget = target + { expr: 'avg(' + target.expr + ')' } + + g.query.prometheus.withLegendFormat('Mean'); + super.new(title, targets=[topTarget, meanTarget], description=description) + + self.withDataLink(instanceLabels, drillDownDashboardUid), + withDataLink(instanceLabels, drillDownDashboardUid): + standardOptions.withLinks( + { + url: 'd/' + drillDownDashboardUid + '?' + std.join('&', std.map(function(l) 'var-%s=${__field.labels.%s}' % [l, l], instanceLabels)) + '&${__url_time_range}', + title: 'Drill down to this instance', + } + ), + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + generic.percentage.stylize(allLayers=false) + + fieldConfig.defaults.custom.withFillOpacity(1) + + fieldConfig.defaults.custom.withLineWidth(1) + + timeSeries.options.legend.withDisplayMode('table') + + timeSeries.options.legend.withPlacement('right') + + timeSeries.options.legend.withCalcsMixin([ + 'mean', + 'max', + 'lastNotNull', + ]) + + timeSeries.standardOptions.withOverrides( + fieldOverride.byName.new('Mean') + + fieldOverride.byName.withPropertiesFromOptions( + fieldConfig.defaults.custom.withLineStyleMixin( + { + fill: 'dash', + dash: [10, 10], + } + ) + + fieldConfig.defaults.custom.withFillOpacity(0) + + timeSeries.standardOptions.color.withMode('fixed') + + timeSeries.standardOptions.color.withFixedColor('light-purple'), + ) + ), +} diff --git a/common-lib/common/panels/memory/stat/base.libsonnet b/common-lib/common/panels/memory/stat/base.libsonnet new file mode 100644 index 000000000..587b02b91 --- /dev/null +++ b/common-lib/common/panels/memory/stat/base.libsonnet @@ -0,0 +1,7 @@ +local g = import '../../../g.libsonnet'; +local stat = g.panel.stat; +local base = import '../../generic/stat/base.libsonnet'; + +base { + +} diff --git a/common-lib/common/panels/memory/stat/main.libsonnet b/common-lib/common/panels/memory/stat/main.libsonnet new file mode 100644 index 000000000..fe214ee12 --- /dev/null +++ b/common-lib/common/panels/memory/stat/main.libsonnet @@ -0,0 +1,5 @@ +{ + base: import './base.libsonnet', + total: import './total.libsonnet', + usage: import './usage.libsonnet', +} diff --git a/common-lib/common/panels/memory/stat/total.libsonnet b/common-lib/common/panels/memory/stat/total.libsonnet new file mode 100644 index 000000000..c230e29c2 --- /dev/null +++ b/common-lib/common/panels/memory/stat/total.libsonnet @@ -0,0 +1,27 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/stat/main.libsonnet'; +local base = import './base.libsonnet'; +local stat = g.panel.stat; +local fieldOverride = g.panel.stat.fieldOverride; +local custom = stat.fieldConfig.defaults.custom; +local defaults = stat.fieldConfig.defaults; +local options = stat.options; +base { + new( + title='Memory total', + targets, + description=||| + Amount of random-access memory (RAM) installed. + It represents the system's available working memory that applications and the operating system use to perform tasks. + A higher memory total generally leads to better system performance and the ability to run more demanding applications and processes simultaneously. + ||| + ): + super.new(title=title, targets=targets, description=description), + + stylize(allLayers=true): + + (if allLayers then super.stylize() else {}) + + + generic.info.stylize(allLayers=false) + + stat.standardOptions.withUnit('bytes'), +} diff --git a/common-lib/common/panels/memory/stat/usage.libsonnet b/common-lib/common/panels/memory/stat/usage.libsonnet new file mode 100644 index 000000000..26f7fb23e --- /dev/null +++ b/common-lib/common/panels/memory/stat/usage.libsonnet @@ -0,0 +1,24 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/stat/main.libsonnet'; +local base = import './base.libsonnet'; +local stat = g.panel.stat; +local fieldOverride = g.panel.stat.fieldOverride; +local custom = stat.fieldConfig.defaults.custom; +local defaults = stat.fieldConfig.defaults; +local options = stat.options; +base { + new( + title='Memory usage', + targets, + description='RAM (random-access memory) currently in use by the operating system and running applications, in percent.' + ): + super.new(title=title, targets=targets, description=description), + + stylize(allLayers=true): + + (if allLayers then super.stylize() else {}) + + + generic.percentage.stylize(allLayers=false) + + stat.standardOptions.withUnit('percent'), + +} diff --git a/common-lib/common/panels/memory/timeSeries/base.libsonnet b/common-lib/common/panels/memory/timeSeries/base.libsonnet new file mode 100644 index 000000000..dae01c389 --- /dev/null +++ b/common-lib/common/panels/memory/timeSeries/base.libsonnet @@ -0,0 +1,10 @@ +local g = import '../../../g.libsonnet'; +local base = import '../../generic/timeSeries/base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + +} diff --git a/common-lib/common/panels/memory/timeSeries/main.libsonnet b/common-lib/common/panels/memory/timeSeries/main.libsonnet new file mode 100644 index 000000000..8a48e365d --- /dev/null +++ b/common-lib/common/panels/memory/timeSeries/main.libsonnet @@ -0,0 +1,5 @@ +{ + base: import './base.libsonnet', + usagePercent: import './usage_percent.libsonnet', + usageBytes: import './usage_bytes.libsonnet', +} diff --git a/common-lib/common/panels/memory/timeSeries/usage_bytes.libsonnet b/common-lib/common/panels/memory/timeSeries/usage_bytes.libsonnet new file mode 100644 index 000000000..623f54b9a --- /dev/null +++ b/common-lib/common/panels/memory/timeSeries/usage_bytes.libsonnet @@ -0,0 +1,26 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/timeSeries/main.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + totalRegexp:: '.*(T|t)otal.*', + new( + title='Memory usage', + targets, + description=||| + RAM (random-access memory) currently in use by the operating system and running applications, in bytes. + |||, + totalRegexp=self.totalRegexp, + ): + super.new(title=title, targets=targets, description=description), + + stylize(allLayers=true, totalRegexp=self.totalRegexp): + (if allLayers then super.stylize() else {}) + + timeSeries.standardOptions.withUnit('bytes') + + timeSeries.standardOptions.withMin(0) + + generic.threshold.stylizeByRegexp(totalRegexp), +} diff --git a/common-lib/common/panels/memory/timeSeries/usage_percent.libsonnet b/common-lib/common/panels/memory/timeSeries/usage_percent.libsonnet new file mode 100644 index 000000000..4f1fa51b1 --- /dev/null +++ b/common-lib/common/panels/memory/timeSeries/usage_percent.libsonnet @@ -0,0 +1,22 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/timeSeries/main.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Memory usage', + targets, + description=||| + RAM (random-access memory) currently in use by the operating system and running applications, in percent. + ||| + ): + super.new(title=title, targets=targets, description=description), + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + generic.percentage.stylize(allLayers=false), +} diff --git a/common-lib/common/panels/network/timeSeries/base.libsonnet b/common-lib/common/panels/network/timeSeries/base.libsonnet new file mode 100644 index 000000000..57ff36754 --- /dev/null +++ b/common-lib/common/panels/network/timeSeries/base.libsonnet @@ -0,0 +1,26 @@ +local g = import '../../../g.libsonnet'; +local base = import '../../generic/timeSeries/base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + + timeSeries.standardOptions.withDecimals(1) + + timeSeries.standardOptions.withUnit('pps'), + + withNegateOutPackets(regexp='/transmit|tx|out/'): + defaults.custom.withAxisLabel('out(-) | in(+)') + + defaults.custom.withAxisCenteredZero(true) + + timeSeries.standardOptions.withOverrides( + fieldOverride.byRegexp.new(regexp) + + fieldOverride.byRegexp.withPropertiesFromOptions( + defaults.custom.withTransform('negative-Y') + ) + ), +} diff --git a/common-lib/common/panels/network/timeSeries/dropped.libsonnet b/common-lib/common/panels/network/timeSeries/dropped.libsonnet new file mode 100644 index 000000000..22700e9cc --- /dev/null +++ b/common-lib/common/panels/network/timeSeries/dropped.libsonnet @@ -0,0 +1,28 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Dropped packets', + targets, + description=||| + Dropped packets occur when data packets traveling through a network are intentionally discarded or lost due to congestion, resource limitations, or network configuration issues. + + Common causes include network congestion, buffer overflows, QoS settings, and network errors, as corrupted or incomplete packets may be discarded by receiving devices. + + Dropped packets can impact network performance and lead to issues such as degraded voice or video quality in real-time applications. + |||, + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + + timeSeries.standardOptions.withNoValue('No dropped packets'), +} diff --git a/common-lib/common/panels/network/timeSeries/errors.libsonnet b/common-lib/common/panels/network/timeSeries/errors.libsonnet new file mode 100644 index 000000000..eb7b313ef --- /dev/null +++ b/common-lib/common/panels/network/timeSeries/errors.libsonnet @@ -0,0 +1,26 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Network errors', + targets, + description=||| + Network errors refer to issues that occur during the transmission of data across a network. + + These errors can result from various factors, including physical issues, jitter, collisions, noise and interference. + + Monitoring network errors is essential for diagnosing and resolving issues, as they can indicate problems with network hardware or environmental factors affecting network quality. + |||, + ): + super.new(title, targets, description) + + self.stylize(), + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + timeSeries.standardOptions.withNoValue('No errors'), +} diff --git a/common-lib/common/panels/network/timeSeries/main.libsonnet b/common-lib/common/panels/network/timeSeries/main.libsonnet new file mode 100644 index 000000000..42ddfd215 --- /dev/null +++ b/common-lib/common/panels/network/timeSeries/main.libsonnet @@ -0,0 +1,8 @@ +{ + base: import './base.libsonnet', + traffic: import './traffic.libsonnet', + errors: import './errors.libsonnet', + dropped: import './dropped.libsonnet', + packets: import './packets.libsonnet', + multicast: import './multicast.libsonnet', +} diff --git a/common-lib/common/panels/network/timeSeries/multicast.libsonnet b/common-lib/common/panels/network/timeSeries/multicast.libsonnet new file mode 100644 index 000000000..c817a463c --- /dev/null +++ b/common-lib/common/panels/network/timeSeries/multicast.libsonnet @@ -0,0 +1,15 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Multicast packets', + targets, + description='Packets sent from one source to multiple recipients simultaneously, allowing efficient one-to-many communication in a network.', + ): + super.new(title, targets, description), +} diff --git a/common-lib/common/panels/network/timeSeries/packets.libsonnet b/common-lib/common/panels/network/timeSeries/packets.libsonnet new file mode 100644 index 000000000..cc136107f --- /dev/null +++ b/common-lib/common/panels/network/timeSeries/packets.libsonnet @@ -0,0 +1,15 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Network packets', + targets, + description='Network packet count tracks the number of data packets transmitted and received over a network connection, providing insight into network activity and performance.', + ): + super.new(title, targets, description), +} diff --git a/common-lib/common/panels/network/timeSeries/traffic.libsonnet b/common-lib/common/panels/network/timeSeries/traffic.libsonnet new file mode 100644 index 000000000..8f87e3d01 --- /dev/null +++ b/common-lib/common/panels/network/timeSeries/traffic.libsonnet @@ -0,0 +1,21 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local timeSeries = g.panel.timeSeries; +local fieldOverride = g.panel.timeSeries.fieldOverride; +local custom = timeSeries.fieldConfig.defaults.custom; +local defaults = timeSeries.fieldConfig.defaults; +local options = timeSeries.options; +base { + new( + title='Network traffic', + targets, + description='Network traffic (bits per sec) measures data transmitted and received.', + ): + super.new(title, targets, description) + + self.stylize(), + + stylize(allLayers=true): + + (if allLayers == true then super.stylize() else {}) + + timeSeries.standardOptions.withUnit('bps'), +} diff --git a/common-lib/common/panels/system/stat/base.libsonnet b/common-lib/common/panels/system/stat/base.libsonnet new file mode 100644 index 000000000..587b02b91 --- /dev/null +++ b/common-lib/common/panels/system/stat/base.libsonnet @@ -0,0 +1,7 @@ +local g = import '../../../g.libsonnet'; +local stat = g.panel.stat; +local base = import '../../generic/stat/base.libsonnet'; + +base { + +} diff --git a/common-lib/common/panels/system/stat/main.libsonnet b/common-lib/common/panels/system/stat/main.libsonnet new file mode 100644 index 000000000..d79009cf1 --- /dev/null +++ b/common-lib/common/panels/system/stat/main.libsonnet @@ -0,0 +1,3 @@ +{ + uptime: import './uptime.libsonnet', +} diff --git a/common-lib/common/panels/system/stat/uptime.libsonnet b/common-lib/common/panels/system/stat/uptime.libsonnet new file mode 100644 index 000000000..d8c45ba26 --- /dev/null +++ b/common-lib/common/panels/system/stat/uptime.libsonnet @@ -0,0 +1,33 @@ +local g = import '../../../g.libsonnet'; +local generic = import '../../generic/stat/main.libsonnet'; +local base = import './base.libsonnet'; +local stat = g.panel.stat; +// Uptime panel. expects duration in seconds as input +base { + new(title='Uptime', targets, description='The duration of time that has passed since the last reboot or system start.'): + super.new(title, targets, description) + + stat.options.withReduceOptions({}) + + stat.options.reduceOptions.withCalcsMixin( + [ + 'lastNotNull', + ] + ) + + self.stylize(), + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + stat.standardOptions.withDecimals(1) + + stat.standardOptions.withUnit('dtdurations') + + stat.options.withColorMode('value') + + stat.options.withGraphMode('none') + + stat.standardOptions.thresholds.withMode('absolute') + + stat.standardOptions.thresholds.withSteps( + [ + // Warn with orange color when uptime resets: + stat.thresholdStep.withColor('orange') + + stat.thresholdStep.withValue(null), + // clear color after 10 minutes: + stat.thresholdStep.withColor('text') + + stat.thresholdStep.withValue(600), + ] + ), +} diff --git a/common-lib/common/panels/system/statusHistory/base.libsonnet b/common-lib/common/panels/system/statusHistory/base.libsonnet new file mode 100644 index 000000000..b3823999c --- /dev/null +++ b/common-lib/common/panels/system/statusHistory/base.libsonnet @@ -0,0 +1,5 @@ +local g = import '../../../g.libsonnet'; +local base = import '../../generic/statusHistory/base.libsonnet'; +base { + +} diff --git a/common-lib/common/panels/system/statusHistory/main.libsonnet b/common-lib/common/panels/system/statusHistory/main.libsonnet new file mode 100644 index 000000000..0cc0937be --- /dev/null +++ b/common-lib/common/panels/system/statusHistory/main.libsonnet @@ -0,0 +1,3 @@ +{ + ntp: import './ntp.libsonnet', +} diff --git a/common-lib/common/panels/system/statusHistory/ntp.libsonnet b/common-lib/common/panels/system/statusHistory/ntp.libsonnet new file mode 100644 index 000000000..42d1c23ba --- /dev/null +++ b/common-lib/common/panels/system/statusHistory/ntp.libsonnet @@ -0,0 +1,28 @@ +local g = import '../../../g.libsonnet'; +local base = import './base.libsonnet'; +local statusHistory = g.panel.statusHistory; +base { + new(title='NTP status', targets, description=''): + super.new(title, targets, description), + + stylize(allLayers=true): + (if allLayers then super.stylize() else {}) + + statusHistory.standardOptions.color.withMode('fixed') + + statusHistory.standardOptions.withMappings( + { + type: 'value', + options: { + '0': { + text: 'Not in sync', + color: 'light-yellow', + index: 1, + }, + '1': { + text: 'In sync', + color: 'light-green', + index: 0, + }, + }, + } + ), +} diff --git a/common-lib/common/panels/system/table/base.libsonnet b/common-lib/common/panels/system/table/base.libsonnet new file mode 100644 index 000000000..c120e15de --- /dev/null +++ b/common-lib/common/panels/system/table/base.libsonnet @@ -0,0 +1,9 @@ +local g = import '../../../g.libsonnet'; +local base = import '../../generic/table/base.libsonnet'; +local table = g.panel.table; +local fieldOverride = g.panel.table.fieldOverride; +local custom = table.fieldConfig.defaults.custom; +local defaults = table.fieldConfig.defaults; +local options = table.options; +base { +} diff --git a/common-lib/common/panels/system/table/main.libsonnet b/common-lib/common/panels/system/table/main.libsonnet new file mode 100644 index 000000000..aa2ce04ff --- /dev/null +++ b/common-lib/common/panels/system/table/main.libsonnet @@ -0,0 +1,4 @@ +{ + base: import './base.libsonnet', + uptime: import './uptime.libsonnet', +} diff --git a/common-lib/common/panels/system/table/uptime.libsonnet b/common-lib/common/panels/system/table/uptime.libsonnet new file mode 100644 index 000000000..31b714e07 --- /dev/null +++ b/common-lib/common/panels/system/table/uptime.libsonnet @@ -0,0 +1,20 @@ +local g = import '../../../g.libsonnet'; +local uptime = import '../stat/uptime.libsonnet'; +local base = import './base.libsonnet'; +local table = g.panel.table; +local fieldOverride = table.fieldOverride; + +base { + local this = self, + + new(): error 'not supported', + stylize(): error 'not supported', + + // when attached to table, this function applies style to row named 'name="Uptime"' + stylizeByName(name='Uptime'): + table.standardOptions.withOverrides( + fieldOverride.byName.new(name) + + fieldOverride.byName.withProperty('custom.cellOptions', { type: 'color-text' }) + + fieldOverride.byName.withPropertiesFromOptions(uptime.stylize(allLayers=false),) + ), +} diff --git a/common-lib/common/utils.libsonnet b/common-lib/common/utils.libsonnet new file mode 100644 index 000000000..7c78f318e --- /dev/null +++ b/common-lib/common/utils.libsonnet @@ -0,0 +1,41 @@ +{ + local this = self, + + labelsToURLvars(labels, prefix):: + std.join('&', ['var-%s=${%s%s}' % [label, prefix, label] for label in labels]), + + // For PromQL or LogQL + labelsToPromQLSelector(labels): std.join(',', ['%s=~"$%s"' % [label, label] for label in labels]), + labelsToLogQLSelector: self.labelsToPromQLSelector, + + labelsToPanelLegend(labels): std.join('/', ['{{%s}}' % [label] for label in labels]), + + toSentenceCase(string):: + std.asciiUpper(string[0]) + std.slice(string, 1, std.length(string), 1), + + // Generate a chain of labels. Useful to create chained variables: + chainLabels(labels, additionalFilters=[]): + local last(arr) = std.reverse(arr)[0]; + local chainSelector(chain) = + std.join( + ',', + additionalFilters + + (if std.length(chain) > 0 + then [this.labelsToPromQLSelector(chain)] + else []) + ); + std.foldl( + function(prev, label) + prev + + [{ + label: label, + chainSelector: chainSelector(self.chain), + chain:: + if std.length(prev) > 0 + then last(prev).chain + [last(prev).label] + else [], + }], + labels, + [] + ), +} diff --git a/common-lib/jsonnetfile.json b/common-lib/jsonnetfile.json new file mode 100644 index 000000000..115d14345 --- /dev/null +++ b/common-lib/jsonnetfile.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "dependencies": [ + { + "source": { + "git": { + "remote": "https://github.com/grafana/grafonnet.git", + "subdir": "gen/grafonnet-v10.0.0" + } + }, + "version": "main" + } + ], + "legacyImports": true +}