Grafana: stop to manage your dashboards manually via UI! (dashboad auto generation, part 1)

Hello all!

Next part: Grafana: stop to manage your dashboards manually via UI! (dashboad auto generation, part 2)

This topc describes a way how to stop to configure your dashboards via Grafana UI.

Grafana originally has built-in way to manage dashboards, using JSON import/export, but this way is hard to name easy. Especially, if you have set of items and many dashboards. Raw JSON management looks not easier as UI way.

Grafana has introduced Grafonnet library to provide some automation for dashboards configuration. Let’s use it!

First of all, we need to prepare our working folder:

# Install jsonnet generator
sudo apt install jsonnet
# Clone Grafana library
git clone git@github.com:grafana/grafonnet-lib.git grafonnet-lib

Now, we can create our dashboard.jsonnet file with standart imports there:

local grafana = import 'grafonnet/grafana.libsonnet';
local dashboard = grafana.dashboard;
local row = grafana.row;
local singlestat = grafana.singlestat;
local graphPanel = grafana.graphPanel;
local influxdb = grafana.influxdb;
local template = grafana.template;

If you have items list to be shared between dashboards, you may include shared files. Let’s include rooms list in my case with file openhab.jsonnet:

{
    rooms: [
        {id: 'kg', title: 'KG'},
        {id: 'sz', title: 'SZ'},
        {id: 'ku', title: 'KU'},
        {id: 'ns', title: 'NS'},
        {id: 'fs', title: 'FS'},
    ],
}

We can include this file:

local openhab = import 'openhab.jsonnet';

Now create a dashboard and add influxdb sources:

# Climate

dashboard.new(
    'Climate',
    uid='openhab_climate',
    tags=['openhab'],
    editable=true,
    time_from='now-24h',
)
.addTemplate(
  grafana.template.datasource(
    'OPENHAB',
    'influxdb',
    'openhab_home',
    hide='label',
  )
)

What we have created here:

  • Dashboard will have title ‘Climate’ and URL id openhab_climate.
  • Default period is 24h.
  • I have datasource type influxdb, id openhab_home and variable name OPENHAB (not really used).

Now let’s add our first graph!

.addRow(
  row.new()
  .addPanel(
    graphPanel.new(
      'Temperature',
      span=12,
      datasource='openhab_home',
      legend_current=true,
      format='°C',
    )
    .addTargets(
        [
            influxdb.target(
                measurement='%(id)s_climate_temperature' % item,
                alias='%(title)s' % item.title,
            )
            .selectField('value').addConverter('mean')
            for item in openhab.rooms
        ],
    )
  )
)

This code loops over openhab.rooms variable (from included file) and fills array with influxdb.target with same parameters as default graph has:

measurment will be set as <item.id>_climate_temperature via loop var.

What we have:

You can also set items directly if you dont like loops:

.addRow(
  row.new()
  .addPanel(
    graphPanel.new(
      'Wind & Rain',
      span=12,
      datasource='openhab_home',
      legend_current=true,
      formatY1='mm/h',
      formatY2='m/s',
    )
    .addTarget(
      influxdb.target(
        measurement='weather_ext_precip',
        alias='Rain',
      )
      .selectField('value').addConverter('mean')
    )
    .addTarget(
      influxdb.target(
        measurement='weather_ext_wind_speed',
        alias='Wind',
      )
      .selectField('value').addConverter('mean')
    )
    .addSeriesOverride( # Set right y-axis for second taget for grafonnet
      {
        'alias': "Wind",
        'yaxis': 2,
      }
    )
  )
)

Also, here is an example of right-size y-axis setup. As result, you will have graph with 2 axes with different values from two items:

Sure, you can also disaply some “last” values, not only time-graph:

.addRow(
  row.new()
  .addPanel(
    gaugePanel.new(
      'Setting',
      datasource='openhab_home',
      allValues=true,
      unit='°C',
      min=5,
      max=35,
      showThresholdMarkers=true,
    )
    .addTargets(
      [
        influxdb.target(
          measurement='%(id)s_heating_thermostat' % item,
          alias='%(title)s' % item.title,
          rawQuery=true,
          query='SELECT last("value") FROM "%(id)s_heating_thermostat" ORDER BY time DESC LIMIT 1 SLIMIT 1' % item,
        )
        .selectField('value').addConverter('last')
        for item in openhab.rooms
      ],
    )
    .addThreshold({ color: 'green', value: 5 })
    .addThreshold({ color: 'yellow', value: 20 })
    .addThreshold({ color: 'red', value: 25 })
  )
)

This will fetch latest values for all rooms thermostat items (ignore current period set).

image

Okay, what next? We have file, now we can generate JSON for it:

jsonnet -J grafonnet-lib dashboard.jsonnet

And you will have now JSON which could be copy-pasted to grafana. But we all hate manual work, this script will populate this graph to our grafana server:

#!/bin/bash -e

# export GRAFANA_URL=http://admin:admin@localhost:3000

for file in dashboard*.jsonnet; do
    echo "Processing: $file"

    JSONNET_PATH=grafonnet-lib \
    jsonnet $file > /tmp/$file.json

    payload="{\"dashboard\": $(jq . /tmp/$file.json), \"overwrite\": true}"

    curl -X POST $BASIC_AUTH \
    -H 'Content-Type: application/json' \
    -d "${payload}" \
    "$GRAFANA_URL/api/dashboards/db"
done

Just call export GRAFANA_URL=http://<username>:<password>@grafana.host.local and call this script. It will auto generate and upload dashboards to your server.

My dashboards (see full example there): @petrows/saltstack-config/dashboards.

This solution has some limitations: it requires to maintain nalso items / devices / rooms / etc lists manually, also not all things could be generated via loops. We will try to improve that in part 2.

Please enjoy and stop configure your dashboards in manual manner!

Next part: Grafana: stop to manage your dashboards manually via UI! (dashboad auto generation, part 2)

4 Likes