Dashboards Available #
What you get #
A Grafana organization of your own, with your logs already wired in ( ADR-0024). Your login is an Editor there, and a member of no other organization. You build folders and dashboards. The platform owns the data sources.
| Data source UID | Reads | |
|---|---|---|
deevnet-logs-workloads | (index, 0), your workloads’ logs | |
deevnet-logs-platform | (index, 1), what the platform says about your tenant | |
deevnet-logs-devices | (index, 2), your devices’ logs from MQTT | the default |
Each one reads with your tenant’s own log read token, so it sees exactly what your token sees and nothing more ( Logs).
The UIDs are the same in every tenant’s organization, on every site, and on the tenant Pi image. A dashboard that names them moves between those places unchanged. Name data sources by these UIDs, never by the numeric id Grafana shows.
The login comes back as attributes of your tenant:
| Attribute | |
|---|---|
dashboard_url | where Grafana is |
dashboard_org_id | your organization |
dashboard_username | your tenant’s name |
dashboard_password | sensitive; in your state, like your log tokens |
A tenant created before dashboards existed gets its login when the operator reconciles it. Ask the operator for the password. After that, your state keeps it.
Logging in #
Open dashboard_url in a browser on the trusted network or from a workload, and sign in with
dashboard_username and dashboard_password. From DVNTM-TD it opens once
CHG-0024’s last step lands. The certificate is the site CA’s, the same
one as the broker’s and the log store’s.
Dashboards as code #
Declare dashboards with the
grafana/grafana
Terraform provider. It reads its connection from the environment, so the same code runs against
Deevnet and against your Pi:
export GRAFANA_URL="$(terraform output -raw dashboard_url)"
export GRAFANA_AUTH="<dashboard_username>:<dashboard_password>"
export GRAFANA_CA_CERT=site-ca.pem
export TF_VAR_grafana_org_id=<dashboard_org_id>
The
kit_env output
writes all four for you.
terraform {
required_providers {
grafana = {
source = "grafana/grafana"
version = "~> 4.46"
}
}
}
provider "grafana" {} # GRAFANA_URL, GRAFANA_AUTH, GRAFANA_CA_CERT
variable "grafana_org_id" {
type = string
}
resource "grafana_folder" "app" {
org_id = var.grafana_org_id
title = "My app"
}
resource "grafana_dashboard" "devices" {
org_id = var.grafana_org_id
folder = grafana_folder.app.uid
config_json = file("${path.module}/devices.json")
}
Put
org_idon every resource. Under a username and password, the provider ignores its ownorg_idsetting andGRAFANA_ORG_ID, and sends organization 1, which you are not a member of. Every create then fails with403 ... folders:create. Tested with provider v4.46.0 against Grafana 13.2.2.
A starter dashboard #
devices.json: a temperature graph taken from your device logs, your device log lines, and your
workload log lines.
{
"uid": "my-devices",
"title": "My devices",
"time": { "from": "now-6h", "to": "now" },
"refresh": "30s",
"panels": [
{
"type": "timeseries",
"title": "Temperature (from device logs)",
"gridPos": { "x": 0, "y": 0, "w": 24, "h": 8 },
"datasource": { "type": "victoriametrics-logs-datasource", "uid": "deevnet-logs-devices" },
"fieldConfig": { "defaults": { "unit": "celsius" }, "overrides": [] },
"targets": [
{
"refId": "A",
"datasource": { "type": "victoriametrics-logs-datasource", "uid": "deevnet-logs-devices" },
"queryType": "statsRange",
"expr": "temp_c:* | stats by (device) avg(temp_c) temp_c",
"legendFormat": "{{device}}"
}
]
},
{
"type": "logs",
"title": "Device logs",
"gridPos": { "x": 0, "y": 8, "w": 24, "h": 10 },
"datasource": { "type": "victoriametrics-logs-datasource", "uid": "deevnet-logs-devices" },
"targets": [
{
"refId": "A",
"datasource": { "type": "victoriametrics-logs-datasource", "uid": "deevnet-logs-devices" },
"queryType": "instant",
"expr": "*"
}
]
},
{
"type": "logs",
"title": "Workload logs",
"gridPos": { "x": 0, "y": 18, "w": 24, "h": 10 },
"datasource": { "type": "victoriametrics-logs-datasource", "uid": "deevnet-logs-workloads" },
"targets": [
{
"refId": "A",
"datasource": { "type": "victoriametrics-logs-datasource", "uid": "deevnet-logs-workloads" },
"queryType": "instant",
"expr": "*"
}
]
}
]
}
The temperature panel needs no metrics service. A device that logs a JSON payload such as
{"msg":"reading","temp_c":21.5} to <tenant>/log/<device> stores temp_c as a field, and
stats by (device) avg(temp_c) turns it into one line per device. Any numeric field works the same
way.
Keep dashboards in code #
A dashboard built only by clicking is not kept. The platform treats Grafana’s own database as
rebuildable: after a rebuild your organization, login and data sources come back, and your
dashboards come back on your next terraform apply. A dashboard you built in the UI does not
come back. Export it (Share โ Export โ Save to file) into your repository and declare it as
above.
What you can’t do #
- Add or change a data source. An Editor can’t. A data source is a URL Grafana’s server requests on your behalf, so only the platform sets them.
- Grafana alerting. It is off. Alerting is a coming platform service.
- Metrics. There is no metrics store yet. Graph numbers from your logs, as above.
- Share a dashboard with another tenant. Organizations don’t share. Copy it through code.