From ff302d03aa70d6de314c6277c58e46fb325dd87d Mon Sep 17 00:00:00 2001 From: fixedit-olatz Date: Mon, 14 Jul 2025 16:15:11 +0200 Subject: [PATCH] Add initial stack files --- .../README.md | 32 + .../docker-compose.prod.yml | 52 + .../docker-compose.yml | 47 + .../helper_scripts/README.md | 7 + .../helper_scripts/install-docker-aws-ec2.sh | 19 + .../dashboards/camera_dashboards.yml | 11 + .../provisioning/dashboards/config_agent.json | 2527 +++++++++++++++++ .../dashboards/device_details.json | 1105 +++++++ .../dashboards/installer_agent_devices.json | 226 ++ .../dashboards/overview_of_devices.json | 1194 ++++++++ .../dashboards/system_overview.json | 1150 ++++++++ .../provisioning/datasources/datasource.yaml | 15 + 12 files changed, 6385 insertions(+) create mode 100644 deployment-and-dashboards-influxdb-v2/README.md create mode 100644 deployment-and-dashboards-influxdb-v2/docker-compose.prod.yml create mode 100644 deployment-and-dashboards-influxdb-v2/docker-compose.yml create mode 100644 deployment-and-dashboards-influxdb-v2/helper_scripts/README.md create mode 100755 deployment-and-dashboards-influxdb-v2/helper_scripts/install-docker-aws-ec2.sh create mode 100644 deployment-and-dashboards-influxdb-v2/provisioning/dashboards/camera_dashboards.yml create mode 100644 deployment-and-dashboards-influxdb-v2/provisioning/dashboards/config_agent.json create mode 100644 deployment-and-dashboards-influxdb-v2/provisioning/dashboards/device_details.json create mode 100644 deployment-and-dashboards-influxdb-v2/provisioning/dashboards/installer_agent_devices.json create mode 100644 deployment-and-dashboards-influxdb-v2/provisioning/dashboards/overview_of_devices.json create mode 100644 deployment-and-dashboards-influxdb-v2/provisioning/dashboards/system_overview.json create mode 100644 deployment-and-dashboards-influxdb-v2/provisioning/datasources/datasource.yaml diff --git a/deployment-and-dashboards-influxdb-v2/README.md b/deployment-and-dashboards-influxdb-v2/README.md new file mode 100644 index 0000000..ce5cbe4 --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/README.md @@ -0,0 +1,32 @@ +# InfluxDB and Grafana stack + +This directory contains a package with the files needed to spin up a stack with InfluxDB and Grafana where Grafana comes pre-populated with multiple dashboards useful for monitoring system metrics from the devices and the propagation of configuration parameters. The stack is using InfluxDB v2 and Flux. + +## Running the stack + +This stack (defined in `docker-compose.yml`) will run: + +- InfluxDB storing time series data (system metrics and configuration parameters) +- Grafana to create dashboards + +To run it: + +1. Run `docker-compose up` +2. Then go to [http://127.0.0.1:8086](http://127.0.0.1:8086) and log in with username `test` and password `testtest` (from the `docker-compose.yml` file) +3. Open grafana UI on [http://127.0.0.1:3000](http://127.0.0.1:3000) and log in with username `admin` and password `test` (from the `docker-compose.yml` file). + +### Example of more secure setup + +The supplied `docker-compose.yml` file is for development usage. For production, you should hande keys in a more secure way. One example of a better setup is to use the `docker-compose.prod.yml` override file: + +```bash +docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d +``` + +## Grafana + +Open the grafana UI on [http://127.0.0.1:3000](http://127.0.0.1:3000), login with user `admin` and password `test` (specified in the [./docker-compose.yml](./docker-compose.yml) file). Click on "Dashboards" and you should see the pre-populated dashboards in the "Cameras" folder. + +## Helper scripts + +The directory [helper_scripts](./helper_scripts/) contains scripts that can assist with deployment, see the [README](./helper_scripts/README.md) for more details. diff --git a/deployment-and-dashboards-influxdb-v2/docker-compose.prod.yml b/deployment-and-dashboards-influxdb-v2/docker-compose.prod.yml new file mode 100644 index 0000000..0f3233b --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/docker-compose.prod.yml @@ -0,0 +1,52 @@ +# This override file: +# - Adds Ouroboros for monitoring, updating, and restarting unhealthy containers +# - Adds healthchecks to InfluxDB and Grafana +# - Configures Slack notifications for status updates +# - Overrides production secrets for InfluxDB and Grafana +# +# This stack is only intended as an example. + +version: "3.8" + +services: + ouroboros: + image: pyouroboros/ouroboros + container_name: ouroboros + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + - INTERVAL=300 # Check every 5 minutes + - CLEANUP=true # Remove old images + - SELF_UPDATE=true # Update Ouroboros itself + - HEALTHCHECK_MONITORING=true # Restart unhealthy containers + - NOTIFIERS=slack://${OUROBOROS_SLACK_HOOK_URL} + labels: + - "com.ouroboros.enable=true" # Allow Ouroboros to watch itself + restart: unless-stopped + + influxdb: + environment: + - DOCKER_INFLUXDB_INIT_USERNAME=${INFLUXDB_USERNAME} + - DOCKER_INFLUXDB_INIT_PASSWORD=${INFLUXDB_PASSWORD} + - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${INFLUXDB_ADMIN_TOKEN} + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8086/health"] + interval: 30s + timeout: 5s + retries: 3 + labels: + - "com.ouroboros.enable=true" # Explicitly allow monitoring + restart: unless-stopped + + grafana: + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD} + - INFLUXDB_TOKEN=${INFLUXDB_ADMIN_TOKEN} + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] + interval: 30s + timeout: 5s + retries: 3 + labels: + - "com.ouroboros.enable=true" # Explicitly allow monitoring + restart: unless-stopped diff --git a/deployment-and-dashboards-influxdb-v2/docker-compose.yml b/deployment-and-dashboards-influxdb-v2/docker-compose.yml new file mode 100644 index 0000000..bcfa304 --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/docker-compose.yml @@ -0,0 +1,47 @@ +# This is a simple example of a stack with InfluxDB and Grafana. +# It is not intended for production use. +# For production use, see an example in docker-compose.prod.yml. + +version: "3.8" + +x-common-vars: &common-vars + INFLUXDB_ORG: &org Whisperer + INFLUXDB_BUCKET: &bucket Cameras + +services: + influxdb: + image: influxdb:2 + container_name: influxdb + ports: + - "8086:8086" + environment: + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_USERNAME: test + DOCKER_INFLUXDB_INIT_PASSWORD: testtest + DOCKER_INFLUXDB_INIT_ORG: *org + DOCKER_INFLUXDB_INIT_BUCKET: *bucket + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: KLoG_Z0NsDIbVzS7zVn_VwhgxUgvwaGWE1wwO9SsGfNEeMaopLMsAA2aAGbCshpetVdu86Ig3-WTKugv6Srg6w== + volumes: + - ./influxdb-data:/var/lib/influxdb2 + + grafana: + image: grafana/grafana + container_name: grafana + ports: + - "3000:3000" + # To avoid permission issues to the mounted dir we run as root... + # Should be changed. See: + # https://grafana.com/docs/grafana/latest/setup-grafana/installation/run-grafana-docker-image/#migrate-to-v51-or-later + user: "0" + environment: + GF_SECURITY_ADMIN_PASSWORD: test + INFLUXDB_TOKEN: KLoG_Z0NsDIbVzS7zVn_VwhgxUgvwaGWE1wwO9SsGfNEeMaopLMsAA2aAGbCshpetVdu86Ig3-WTKugv6Srg6w== + INFLUXDB_ORG: *org + INFLUXDB_BUCKET: *bucket + volumes: + - ./grafana-data:/var/lib/grafana + # The provisioning folder contains the data source info. + # This automatically sets up InfluxDB as a data source. + - ./provisioning:/etc/grafana/provisioning + depends_on: + - influxdb diff --git a/deployment-and-dashboards-influxdb-v2/helper_scripts/README.md b/deployment-and-dashboards-influxdb-v2/helper_scripts/README.md new file mode 100644 index 0000000..e09ebeb --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/helper_scripts/README.md @@ -0,0 +1,7 @@ +# Helper scripts + +## [install-docker-aws-ec2.sh](./install-docker-aws-ec2.sh) + +This script can be used to install docker with docker compose on an AWS EC2 instance with Amazon Linux 2. This makes it easy to deploy the example dashboard to an AWS EC2 instance. + +Just copy the script to the EC2 instance and run it with `./install-docker-aws-ec2.sh`. diff --git a/deployment-and-dashboards-influxdb-v2/helper_scripts/install-docker-aws-ec2.sh b/deployment-and-dashboards-influxdb-v2/helper_scripts/install-docker-aws-ec2.sh new file mode 100755 index 0000000..a4a0383 --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/helper_scripts/install-docker-aws-ec2.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -euo pipefail + +# Install Docker +sudo yum update -y +sudo amazon-linux-extras enable docker +sudo yum install -y docker +sudo systemctl start docker +sudo systemctl enable docker +sudo usermod -aG docker $USER + +# Install Docker Compose as a plugin +DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} +mkdir -p "$DOCKER_CONFIG/cli-plugins" + +curl -SL https://github.com/docker/compose/releases/download/v2.37.3/docker-compose-linux-x86_64 \ + -o "$DOCKER_CONFIG/cli-plugins/docker-compose" + +chmod +x "$DOCKER_CONFIG/cli-plugins/docker-compose" \ No newline at end of file diff --git a/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/camera_dashboards.yml b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/camera_dashboards.yml new file mode 100644 index 0000000..ad419eb --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/camera_dashboards.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: + - name: 'Camera monitoring dashboards' + folder: 'Cameras' + type: file + disableDeletion: false + editable: true + options: + # Look for dashboards in this folder (json files) + path: /etc/grafana/provisioning/dashboards \ No newline at end of file diff --git a/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/config_agent.json b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/config_agent.json new file mode 100644 index 0000000..2eb07ef --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/config_agent.json @@ -0,0 +1,2527 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Camera configuration changes and errors", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 12, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 11, + "panels": [], + "title": "Config Agent", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.5, + "drawStyle": "bars", + "fillOpacity": 57, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/.* - Failed/" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Sync No Diff - Successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Sync With Diff - Successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Sync Completed - Successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 17, + "x": 0, + "y": 1 + }, + "id": 12, + "interval": "1s", + "maxDataPoints": 100000, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [ + { + "params": ["1d"], + "type": "time" + } + ], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"task\" and\n r.source_app == \"ConfigAgent\" and\n (r.name == \"sync_with_diff\" or r.name == \"sync_no_diff\" or r.name == \"sync_completed\") and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n |> map(fn: (r) => ({\n r with\n result: if r.success == \"true\" then \"Successful\" else \"Failed\",\n series: r.name + \" - \" + (if r.success == \"true\" then \"Successful\" else \"Failed\")\n }))\n |> group(columns: [\"series\"])\n |> aggregateWindow(\n every: ${sync_window_size},\n fn: count,\n column: \"_value\",\n createEmpty: true\n )\n |> fill(column: \"_value\", value: 0)\n |> rename(columns: {_value: \"count\"})\n |> yield(name: \"sync_counts\")\n", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["error_reason"], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [ + { + "key": "success::tag", + "operator": "=", + "value": "false" + }, + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "change" + } + ] + } + ], + "title": "Syncs With and Without Diff", + "transformations": [ + { + "id": "renameByRegex", + "options": { + "regex": ".*sync_(with_diff|no_diff|completed) - (Successful|Failed).*", + "renamePattern": "Sync $1 - $2" + } + }, + { + "id": "renameByRegex", + "options": { + "regex": "(.*)no_diff(.*)", + "renamePattern": "$1No Diff$2" + } + }, + { + "id": "renameByRegex", + "options": { + "regex": "(.*)with_diff(.*)", + "renamePattern": "$1With Diff$2" + } + }, + { + "id": "renameByRegex", + "options": { + "regex": "(.*)completed(.*)", + "renamePattern": "$1Completed$2" + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [] + } + ] + }, + "gridPos": { + "h": 7, + "w": 7, + "x": 17, + "y": 1 + }, + "id": 3, + "interval": "1s", + "maxDataPoints": 100000, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelMaxLength": 7, + "xTickLabelRotation": 75, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [ + { + "params": ["1d"], + "type": "time" + } + ], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // 1) Filter to exactly the events you want\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.success == \"false\" and\n r.category == \"change\" and\n r.source_app == \"ConfigAgent\" and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n\n // 2) Ungroup everything so count() sees all rows as one series\n |> group()\n\n // 3) Roll up into 1-day buckets, counting raw rows\n |> aggregateWindow(\n every: ${sync_window_size},\n fn: count,\n column: \"_value\",\n createEmpty: true // emit a point (with null) even if no events\n )\n\n // 4) Replace nulls with 0 so days without events show as zero\n |> fill(column: \"_value\", value: 0)\n\n // 5) (Optional) Rename for clarity\n |> rename(columns: {_value: \"daily_count\"})\n\n |> yield(name: \"daily_message_count\")\n", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["error_reason"], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [ + { + "key": "success::tag", + "operator": "=", + "value": "false" + }, + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "change" + } + ] + } + ], + "title": "Failed Config Changes Count", + "type": "barchart" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "color-text" + }, + "filterable": false, + "inspect": false + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "custom.width", + "value": 192 + }, + { + "id": "displayName", + "value": "Date" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "description_VideoConfig change" + }, + "properties": [ + { + "id": "displayName", + "value": "Change description" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error_reason_VideoConfig change" + }, + "properties": [ + { + "id": "displayName", + "value": "Error description" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "reason_VideoConfig change" + }, + "properties": [ + { + "id": "displayName", + "value": "Reason for change" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Change description" + }, + "properties": [ + { + "id": "custom.width", + "value": 212 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "description_HandleACAP change" + }, + "properties": [ + { + "id": "custom.width", + "value": 387 + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*description_.*/" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "~^\\[FAILED\\]": { + "color": "dark-red", + "index": 0, + "text": "Failed" + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "success_flag" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "FAILED": { + "color": "dark-red", + "index": 1 + }, + "SUCCESS": { + "color": "dark-green", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Result" + }, + "properties": [ + { + "id": "custom.width", + "value": 104 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Date" + }, + "properties": [ + { + "id": "custom.width", + "value": 172 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Change Category" + }, + "properties": [ + { + "id": "custom.width", + "value": 152 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Target" + }, + "properties": [ + { + "id": "custom.width", + "value": 203 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Old Value" + }, + "properties": [ + { + "id": "custom.width", + "value": 155 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.width", + "value": 272 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error_reason" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "New Value" + }, + "properties": [ + { + "id": "custom.width", + "value": 151 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "custom.width", + "value": 169 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device detail link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + }, + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 5, + "interval": "1m", + "maxDataPoints": 100000, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 1, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"change\" and\n r.source_app == \"ConfigAgent\" and\n (\n \"${ca_success_filter}\" == \".*\" or\n (\"${ca_success_filter}\" == \"Successful\" and r.success == \"true\") or\n (\"${ca_success_filter}\" == \"Failed\" and r.success == \"false\")\n ) and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/ and\n exists r[\"name\"] and\n r[\"name\"] =~ /${name_filter:regex}/\n )\n |> map(fn: (r) => ({\n r with\n // success_flag is used to show the result in a nice way\n success_flag: if r.success == \"true\" then \"SUCCESS\" else \"FAILED\",\n // \"target\" does not exist for all change categories, so we need to\n // set a default to prevent the query from failing\n target: if exists r.target then r.target else \"\"\n }))\n |> keep(columns: [\"_time\", \"_field\", \"_value\", \"name\", \"${unique_identifier}\", \"success_flag\", \"target\"])\n |> rename(columns: {\n // Rename to Device since we use that to create links\n \"${unique_identifier}\": \"Device\"\n })\n |> group()\n |> pivot(\n rowKey: [\"_time\", \"name\", \"Device\", \"success_flag\", \"target\"], \n columnKey: [\"_field\"], \n valueColumn: \"_value\"\n)\n\n |> sort(columns: [\"_time\"], desc: true)\n |> yield(name: \"all\")\n", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["error_reason"], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "success::tag", + "operator": "=", + "value": "false" + }, + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "change" + } + ] + } + ], + "title": "List of Changes", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": { + "Device": 1, + "_time": 0, + "description": 7, + "error_reason": 9, + "name": 2, + "new_value": 6, + "old_value": 5, + "reason": 8, + "success_flag": 3, + "target": 4 + }, + "renameByName": { + "description": "Description", + "error_reason": "Failure Reason", + "host": "Device", + "name": "Change Category", + "new_value": "New Value", + "old_value": "Old Value", + "reason": "Change Reason", + "success_flag": "Result", + "target": "Target" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "color-text" + }, + "filterable": false, + "inspect": false + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "custom.width", + "value": 192 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "custom.width", + "value": 217 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "displayName", + "value": "Date" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "description_internal_error" + }, + "properties": [ + { + "id": "displayName", + "value": "Description" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "severity_internal_error" + }, + "properties": [ + { + "id": "displayName", + "value": "Severity" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.width", + "value": 828 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Date" + }, + "properties": [ + { + "id": "custom.width", + "value": 211 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device detail link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + }, + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 18, + "x": 0, + "y": 15 + }, + "id": 15, + "interval": "1m", + "maxDataPoints": 100000, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 18, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Date" + } + ] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"internal_error\" and\n r.source_app == \"ConfigAgent\" and\n // Filter on common device filters...\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/ and\n exists r[\"name\"] and\n r[\"name\"] =~ /${name_filter:regex}/\n )\n |> keep(columns: [\"_time\", \"category\", \"${unique_identifier}\", \"_field\", \"_value\"])\n |> rename(columns: {\n \"${unique_identifier}\": \"Device\"\n })\n |> group()\n |> pivot(\n rowKey: [\"_time\", \"Device\"],\n columnKey: [\"_field\", \"category\"],\n valueColumn: \"_value\"\n )\n |> sort(columns: [\"_time\"], desc: true)\n |> yield(name: \"all\")\n", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["error_reason"], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "success::tag", + "operator": "=", + "value": "false" + }, + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "change" + } + ] + } + ], + "title": "List of Internal Errors", + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 15 + }, + "id": 6, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "orientation": "auto", + "showValue": "always", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelMaxLength": 7, + "xTickLabelRotation": 75, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [ + { + "params": ["1d"], + "type": "time" + } + ], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1) Filter to exactly the events you want\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.source_app == \"ConfigAgent\" and\n r.category == \"internal_error\" and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n\n // 2) Ungroup everything so count() sees all rows as one series\n |> group()\n\n // 3) Roll up into 1-day buckets, counting raw rows\n |> aggregateWindow(\n every: ${sync_window_size},\n fn: count,\n column: \"_value\",\n createEmpty: true // emit a point (with null) even if no events\n )\n\n // 4) Replace nulls with 0 so days without events show as zero\n |> fill(column: \"_value\", value: 0)\n\n // 5) (Optional) Rename for clarity\n |> rename(columns: {_value: \"daily_count\"})\n\n |> yield(name: \"daily_message_count\")\n", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["severity"], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [ + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "internal_error" + } + ] + } + ], + "title": "Internal Error Count", + "type": "barchart" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 21 + }, + "id": 10, + "panels": [], + "title": "Installer Agent", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 22 + }, + "id": 8, + "options": { + "displayLabels": ["name", "value"], + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true, + "values": [] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1) Only the user_data values from the sync messages\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"task\" and\n r.source_app == \"InstallerAgent\" and\n r.success == \"true\" and\n r._field == \"user_data\" and\n r.name == \"sync_no_diff\" and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n\n // 2) Get the last value per host\n |> last(column: \"_value\")\n\n // 3) Map each to count = 1\n |> map(fn: (r) => ({\n user_data: r._value,\n one: 1\n }))\n\n // 4) Group by user_data and sum the counts\n |> group(columns: [\"user_data\"])\n |> sum(column: \"one\")\n\n // 5) Pivot so Grafana sees one row, one column per user_data\n |> pivot(\n rowKey: [],\n columnKey: [\"user_data\"],\n valueColumn: \"one\"\n )\n |> yield(name: \"pie_data\")\n", + "refId": "A" + } + ], + "title": "Deployment File Versions In Use", + "type": "piechart" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.5, + "drawStyle": "bars", + "fillOpacity": 57, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/.* - Failed/" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Sync No Diff - Successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Sync With Diff - Successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Sync Completed - Successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 6, + "y": 22 + }, + "id": 9, + "interval": "1s", + "maxDataPoints": 100000, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [ + { + "params": ["1d"], + "type": "time" + } + ], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"task\" and\n r.source_app == \"InstallerAgent\" and\n (r.name == \"sync_with_diff\" or r.name == \"sync_no_diff\" or r.name == \"sync_completed\") and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n |> map(fn: (r) => ({\n r with\n result: if r.success == \"true\" then \"Successful\" else \"Failed\",\n series: r.name + \" - \" + (if r.success == \"true\" then \"Successful\" else \"Failed\")\n }))\n |> group(columns: [\"series\"])\n |> aggregateWindow(\n every: ${sync_window_size},\n fn: count,\n column: \"_value\",\n createEmpty: true\n )\n |> fill(column: \"_value\", value: 0)\n |> rename(columns: {_value: \"count\"})\n |> yield(name: \"sync_counts\")\n", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["error_reason"], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [ + { + "key": "success::tag", + "operator": "=", + "value": "false" + }, + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "change" + } + ] + } + ], + "title": "Syncs With and Without Diff", + "transformations": [ + { + "id": "renameByRegex", + "options": { + "regex": ".*sync_(with_diff|no_diff|completed) - (Successful|Failed).*", + "renamePattern": "Sync $1 - $2" + } + }, + { + "id": "renameByRegex", + "options": { + "regex": "(.*)no_diff(.*)", + "renamePattern": "$1No Diff$2" + } + }, + { + "id": "renameByRegex", + "options": { + "regex": "(.*)with_diff(.*)", + "renamePattern": "$1With Diff$2" + } + }, + { + "id": "renameByRegex", + "options": { + "regex": "(.*)completed(.*)", + "renamePattern": "$1Completed$2" + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 22 + }, + "id": 13, + "interval": "1s", + "maxDataPoints": 100000, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelMaxLength": 7, + "xTickLabelRotation": 75, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [ + { + "params": ["1d"], + "type": "time" + } + ], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // 1) Filter to exactly the events you want\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.success == \"false\" and\n r.category == \"change\" and\n r.source_app == \"InstallerAgent\" and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n\n // 2) Ungroup everything so count() sees all rows as one series\n |> group()\n\n // 3) Roll up into 1-day buckets, counting raw rows\n |> aggregateWindow(\n every: ${sync_window_size},\n fn: count,\n column: \"_value\",\n createEmpty: true // emit a point (with null) even if no events\n )\n\n // 4) Replace nulls with 0 so days without events show as zero\n |> fill(column: \"_value\", value: 0)\n\n // 5) (Optional) Rename for clarity\n |> rename(columns: {_value: \"daily_count\"})\n\n |> yield(name: \"daily_message_count\")\n", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["error_reason"], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [ + { + "key": "success::tag", + "operator": "=", + "value": "false" + }, + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "change" + } + ] + } + ], + "title": "Failed Changes Count", + "type": "barchart" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "color-text" + }, + "filterable": false, + "inspect": false + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "custom.width", + "value": 192 + }, + { + "id": "displayName", + "value": "Date" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "description_VideoConfig change" + }, + "properties": [ + { + "id": "displayName", + "value": "Change description" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error_reason_VideoConfig change" + }, + "properties": [ + { + "id": "displayName", + "value": "Error description" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "reason_VideoConfig change" + }, + "properties": [ + { + "id": "displayName", + "value": "Reason for change" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Change description" + }, + "properties": [ + { + "id": "custom.width", + "value": 212 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "description_HandleACAP change" + }, + "properties": [ + { + "id": "custom.width", + "value": 387 + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*description_.*/" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "~^\\[FAILED\\]": { + "color": "dark-red", + "index": 0, + "text": "Failed" + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "success_flag" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "FAILED": { + "color": "dark-red", + "index": 1 + }, + "SUCCESS": { + "color": "dark-green", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error_reason" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Change Category" + }, + "properties": [ + { + "id": "custom.width", + "value": 144 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Result" + }, + "properties": [ + { + "id": "custom.width", + "value": 99 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Target" + }, + "properties": [ + { + "id": "custom.width", + "value": 171 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Old Value" + }, + "properties": [ + { + "id": "custom.width", + "value": 174 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "New Value" + }, + "properties": [ + { + "id": "custom.width", + "value": 185 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.width", + "value": 319 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "custom.width", + "value": 169 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device detail link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + }, + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 14, + "interval": "1m", + "maxDataPoints": 100000, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 18, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"change\" and\n r.source_app == \"InstallerAgent\" and\n (\n \"${ca_success_filter}\" == \".*\" or\n (\"${ca_success_filter}\" == \"Successful\" and r.success == \"true\") or\n (\"${ca_success_filter}\" == \"Failed\" and r.success == \"false\")\n ) and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/ and\n exists r[\"name\"] and\n r[\"name\"] =~ /${name_filter:regex}/\n )\n |> map(fn: (r) => ({\n r with\n // success_flag is used to show the result in a nice way\n success_flag: if r.success == \"true\" then \"SUCCESS\" else \"FAILED\",\n // \"target\" does not exist for all change categories, so we need to\n // set a default to prevent the query from failing\n target: if exists r.target then r.target else \"\"\n }))\n |> keep(columns: [\"_time\", \"_field\", \"_value\", \"name\", \"${unique_identifier}\", \"success_flag\", \"target\"])\n |> rename(columns: {\n // Rename to Device since we use that to create links\n \"${unique_identifier}\": \"Device\"\n })\n |> group()\n |> pivot(\n rowKey: [\"_time\", \"name\", \"Device\", \"success_flag\", \"target\"], \n columnKey: [\"_field\"], \n valueColumn: \"_value\"\n)\n\n |> sort(columns: [\"_time\"], desc: true)\n |> yield(name: \"all\")\n", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["error_reason"], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "success::tag", + "operator": "=", + "value": "false" + }, + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "change" + } + ] + } + ], + "title": "List of Changes", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": { + "Device": 1, + "_time": 0, + "description": 7, + "error_reason": 9, + "name": 2, + "new_value": 6, + "old_value": 5, + "reason": 8, + "success_flag": 3, + "target": 4 + }, + "renameByName": { + "description": "Description", + "error_reason": "Failure Reason", + "host": "Device", + "name": "Change Category", + "new_value": "New Value", + "old_value": "Old Value", + "reason": "Change Reason", + "success_flag": "Result", + "target": "Target" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "color-text" + }, + "filterable": false, + "inspect": false + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "custom.width", + "value": 192 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "custom.width", + "value": 217 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "displayName", + "value": "Date" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "description_internal_error" + }, + "properties": [ + { + "id": "displayName", + "value": "Description" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "severity_internal_error" + }, + "properties": [ + { + "id": "displayName", + "value": "Severity" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.width", + "value": 828 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Date" + }, + "properties": [ + { + "id": "custom.width", + "value": 211 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device detail link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + }, + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 18, + "x": 0, + "y": 38 + }, + "id": 2, + "interval": "1m", + "maxDataPoints": 100000, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 18, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Date" + } + ] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"internal_error\" and\n r.source_app == \"InstallerAgent\" and\n // Filter on common device filters...\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/ and\n exists r[\"name\"] and\n r[\"name\"] =~ /${name_filter:regex}/\n )\n |> keep(columns: [\"_time\", \"category\", \"${unique_identifier}\", \"_field\", \"_value\"])\n |> rename(columns: {\n \"${unique_identifier}\": \"Device\"\n })\n |> group()\n |> pivot(\n rowKey: [\"_time\", \"Device\"],\n columnKey: [\"_field\", \"category\"],\n valueColumn: \"_value\"\n )\n |> sort(columns: [\"_time\"], desc: true)\n |> yield(name: \"all\")\n", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["error_reason"], + "type": "field" + } + ] + ], + "tags": [ + { + "key": "success::tag", + "operator": "=", + "value": "false" + }, + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "change" + } + ] + } + ], + "title": "List of Internal Errors", + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 18, + "y": 38 + }, + "id": 16, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "orientation": "auto", + "showValue": "always", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xTickLabelMaxLength": 7, + "xTickLabelRotation": 75, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [ + { + "params": ["1d"], + "type": "time" + } + ], + "measurement": "messages", + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1) Filter to exactly the events you want\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.source_app == \"InstallerAgent\" and\n r.category == \"internal_error\" and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n\n // 2) Ungroup everything so count() sees all rows as one series\n |> group()\n\n // 3) Roll up into 1-day buckets, counting raw rows\n |> aggregateWindow(\n every: ${sync_window_size},\n fn: count,\n column: \"_value\",\n createEmpty: true // emit a point (with null) even if no events\n )\n\n // 4) Replace nulls with 0 so days without events show as zero\n |> fill(column: \"_value\", value: 0)\n\n // 5) (Optional) Rename for clarity\n |> rename(columns: {_value: \"daily_count\"})\n\n |> yield(name: \"daily_message_count\")\n", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["severity"], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [ + { + "condition": "AND", + "key": "category::tag", + "operator": "=", + "value": "internal_error" + } + ] + } + ], + "title": "Internal Error Count", + "type": "barchart" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [ + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "$__all", + "value": "$__all" + }, + "description": "", + "includeAll": true, + "label": "Change Success", + "name": "ca_success_filter", + "options": [ + { + "selected": false, + "text": "Successful", + "value": "Successful" + }, + { + "selected": false, + "text": "Failed", + "value": "Failed" + } + ], + "query": "Successful,Failed", + "type": "custom" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(\n bucket: \"${bucket}\",\n tag: \"name\",\n predicate: (r) => \n r._measurement == \"messages\" and \n r.category == \"change\",\n start: -10y\n)\n", + "includeAll": true, + "label": "Change Category", + "multi": true, + "name": "name_filter", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(\n bucket: \"${bucket}\",\n tag: \"name\",\n predicate: (r) => \n r._measurement == \"messages\" and \n r.category == \"change\",\n start: -10y\n)\n" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"geography\")", + "includeAll": true, + "label": "Geography", + "multi": true, + "name": "geography", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"geography\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "definition": "buckets()", + "hide": 2, + "label": "Database", + "name": "bucket", + "options": [], + "query": { + "query": "buckets()" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"region\")", + "includeAll": true, + "label": "Region", + "multi": true, + "name": "region", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"region\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"area\")", + "includeAll": true, + "label": "Area", + "multi": true, + "name": "area", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"area\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"site\")", + "description": "", + "includeAll": true, + "label": "Site", + "multi": true, + "name": "site", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"site\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"type\")", + "includeAll": true, + "label": "Type", + "multi": true, + "name": "type", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"type\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"${unique_identifier}\")", + "includeAll": true, + "label": "Device", + "multi": true, + "name": "device", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"${unique_identifier}\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "current": { + "text": "host", + "value": "host" + }, + "description": "The name of the field for the unique identifier like the serial number", + "hide": 2, + "name": "unique_identifier", + "query": "host", + "skipUrlSync": true, + "type": "constant" + }, + { + "allowCustomValue": false, + "current": { + "text": "InfluxDB", + "value": "InfluxDB" + }, + "hide": 2, + "name": "datasource", + "options": [], + "query": "influxdb", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"product_full_name\")", + "includeAll": true, + "label": "Model Name", + "multi": true, + "name": "model", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"product_full_name\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allowCustomValue": false, + "current": { + "text": "1d", + "value": "1d" + }, + "description": "The duration to aggregate to one bucket for the sync counts", + "label": "Sync Bucket Size", + "name": "sync_window_size", + "options": [ + { + "selected": true, + "text": "1d", + "value": "1d" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "15m", + "value": "15m" + }, + { + "selected": false, + "text": "1m", + "value": "1m" + } + ], + "query": "1d, 1h, 15m, 1m", + "type": "custom" + }, + { + "current": { + "text": "bekds38txl1j4c", + "value": "bekds38txl1j4c" + }, + "description": "UID of the device details dashboard", + "hide": 2, + "name": "device_details_uid", + "query": "bekds38txl1j4c", + "skipUrlSync": true, + "type": "constant" + } + ] + }, + "time": { + "from": "now-7d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Config Agent", + "uid": "uniquestringaa", + "version": 57 +} \ No newline at end of file diff --git a/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/device_details.json b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/device_details.json new file mode 100644 index 0000000..1f92d26 --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/device_details.json @@ -0,0 +1,1105 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4, + "links": [], + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "// Get the latest timestamp where product_full_name was reported for the selected camera\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n // Filter on measurement to get a consistent schema\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n // Create a stream for each device\n |> group(columns: [\"${unique_identifier}\"])\n // Sort and get the latest\n |> sort(columns: [\"_time\"], desc: true)\n |> first()\n |> keep(columns: [\"${unique_identifier}\", \"product_full_name\"])", + "refId": "A" + } + ], + "title": "Device Model Name", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 3 + }, + "id": 11, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "from(bucket: \"${bucket}\")\n // Time range filter...\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // Generic measurement just to access the tags...\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n\n // Filter based on drop-downs\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n\n // Sort and take the latest received tags for each device\n |> group(columns: [\"${unique_identifier}\"])\n |> sort(columns: [\"_time\"], desc: true)\n |> limit(n:1)\n\n // Keep things we want only\n |> keep(columns: [\"_time\", \"${unique_identifier}\", \"area\", \"geography\", \"region\", \"site\", \"type\", \"product_full_name\"])\n\n // Flatten\n |> group()\n", + "refId": "A" + } + ], + "title": "Geo Tags", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "_time": true, + "product_full_name": true, + "type": true + }, + "includeByName": {}, + "indexByName": { + "_time": 7, + "area": 6, + "geography": 1, + "host": 0, + "product_full_name": 2, + "region": 3, + "site": 4, + "type": 5 + }, + "renameByName": { + "area": "Area", + "geography": "Geography", + "host": "Device", + "product_full_name": "", + "region": "Region", + "site": "Site" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "axisPlacement": "auto", + "fillOpacity": 77, + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineWidth": 3, + "spanNulls": false + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 10, + "options": { + "alignValue": "left", + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "mergeValues": true, + "rowHeight": 0.78, + "showValue": "auto", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // Generic measurement just to access the tags. To reduce number of points,\n // we also filter to one single core and field.\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"cpu\" and\n r[\"cpu\"] == \"cpu-total\" and\n r[\"_field\"] == \"usage_idle\"\n )\n\n // Filter for matching devices\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n\n // Aggregate into time series windows to reduce number of returned data points\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false)\n\n // Keep timestamp, device and type\n |> keep(columns: [\"_time\", \"${unique_identifier}\", \"type\"])\n\n // Rename and remap to Grafana-friendly _time, _field, _value,\n // that gets rid of the \"type \" prefix in the visualization.\n |> map(fn: (r) => ({\n _time: r._time,\n _field: r[\"${unique_identifier}\"],\n _value: r[\"type\"]\n }))\n\n // Group per device (now _field)\n |> group(columns: [\"_field\"])\n\n // Sort by time\n |> sort(columns: [\"_time\"])\n", + "refId": "B" + } + ], + "title": "Device Type Tag Over Time", + "type": "state-timeline" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 4, + "panels": [], + "title": "Device Metrics", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 3600 + }, + { + "color": "#EAB839", + "value": 86400 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": " from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"system\")\n |> filter(fn: (r) => r[\"_field\"] == \"uptime\")\n |> group(columns: [\"${unique_identifier}\"]) // flatten tag sets for host\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)", + "refId": "A" + } + ], + "title": "Device Uptime", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 3600000, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "dashed" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 50000000 + }, + { + "color": "#EAB839", + "value": 100000000 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": " from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"mem\")\n |> filter(fn: (r) => r[\"_field\"] == \"available\")\n |> group(columns: [\"${unique_identifier}\"]) // flatten tag sets for host\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)", + "refId": "A" + } + ], + "title": "Free RAM", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 3600000, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "dashed" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 10 + }, + { + "color": "#EAB839", + "value": 20 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": " from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n |> filter(fn: (r) => r[\"_field\"] == \"usage_idle\")\n |> group(columns: [\"${unique_identifier}\", \"cpu\"]) // flatten tag sets for host\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n ", + "refId": "A" + } + ], + "title": "Free CPU", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 3600000, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "dashed" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "decbytes" + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/Total.*/" + }, + "properties": [ + { + "id": "custom.fillOpacity", + "value": 11 + }, + { + "id": "custom.showPoints", + "value": "never" + }, + { + "id": "color", + "value": { + "fixedColor": "#ffffff", + "mode": "shades" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 30 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "free = \nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"disk\")\n |> filter(fn: (r) => r[\"_field\"] == \"free\")\n |> filter(fn: (r) => r[\"path\"] == \"/usr/local\")\n |> group(columns: [\"${unique_identifier}\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\", \"${unique_identifier}\"])\n |> rename(columns: {_value: \"Free\"})\n\ntotal =\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"disk\")\n |> filter(fn: (r) => r[\"_field\"] == \"total\")\n |> filter(fn: (r) => r[\"path\"] == \"/usr/local\")\n |> group(columns: [\"${unique_identifier}\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\", \"${unique_identifier}\"])\n |> rename(columns: {_value: \"Total\"})\n\njoin(\n tables: {free: free, total: total},\n on: [\"_time\", \"${unique_identifier}\"]\n)\n", + "refId": "A" + } + ], + "title": "Free Flash Memory", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 3600000, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "dashed" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "decbytes" + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/Total.*/" + }, + "properties": [ + { + "id": "custom.fillOpacity", + "value": 11 + }, + { + "id": "custom.showPoints", + "value": "never" + }, + { + "id": "color", + "value": { + "fixedColor": "#ffffff", + "mode": "shades" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 30 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "free = \nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"disk\")\n |> filter(fn: (r) => r[\"_field\"] == \"free\")\n |> filter(fn: (r) => r[\"path\"] == \"/var/spool/storage/SD_DISK\")\n |> group(columns: [\"${unique_identifier}\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\", \"${unique_identifier}\"])\n |> rename(columns: {_value: \"Free\"})\n\ntotal =\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"disk\")\n |> filter(fn: (r) => r[\"_field\"] == \"total\")\n |> filter(fn: (r) => r[\"path\"] == \"/var/spool/storage/SD_DISK\")\n |> group(columns: [\"${unique_identifier}\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\", \"${unique_identifier}\"])\n |> rename(columns: {_value: \"Total\"})\n\njoin(\n tables: {free: free, total: total},\n on: [\"_time\", \"${unique_identifier}\"]\n)\n", + "refId": "A" + } + ], + "title": "Free SD Card Storage", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 38 + }, + "id": 6, + "panels": [], + "title": "Network Info", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 39 + }, + "id": 7, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": " // Get latest local and external IP per device in one query\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n // Select both relevant measurements\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"local_ip_addresses\" or\n r[\"_measurement\"] == \"external_ip\"\n )\n // Group by device and measurement\n |> group(columns: [\"${unique_identifier}\", \"_measurement\"])\n // Get the latest entry for each\n |> last()\n // Reshape for clarity\n |> pivot(\n rowKey: [\"${unique_identifier}\"],\n columnKey: [\"_measurement\"],\n valueColumn: \"_value\"\n )\n // Optional: Rename columns for clarity\n |> rename(columns: {\n local_ip_addresses: \"Local IPs\",\n external_ip: \"External IP\"\n })\n |> keep(columns: [\"${unique_identifier}\", \"Local IPs\", \"External IP\"])", + "refId": "A" + } + ], + "title": "Device IP", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 17, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 42 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"internet_connectivity_dns\")\n |> filter(fn: (r) => r[\"result\"] == \"success\")\n |> filter(fn: (r) => r[\"_field\"] == \"query_time_ms\")\n |> group(columns: [\"${unique_identifier}\"]) // flatten tag sets for host\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)", + "refId": "A" + } + ], + "title": "DNS Response Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 64, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 3600000, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 4, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 52 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"internet_connectivity_dns\")\n // Map success to 1, others to 0\n |> map(fn: (r) => ({\n r with _value: if r[\"result\"] == \"success\" then 1 else 0\n }))\n |> group(columns: [\"${unique_identifier}\"])\n // Calculate the mean for each block to reduce number of data points\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n", + "refId": "A" + } + ], + "title": "DNS Connection OK", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [ + { + "allowCustomValue": false, + "current": { + "text": [], + "value": [] + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\nschema.tagValues(\n bucket: \"${bucket}\",\n tag: \"host\",\n)", + "label": "Device", + "multi": true, + "name": "device", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\nschema.tagValues(\n bucket: \"${bucket}\",\n tag: \"host\",\n)" + }, + "refresh": 1, + "regex": "", + "sort": 5, + "type": "query" + }, + { + "current": { + "text": "host", + "value": "host" + }, + "description": "", + "hide": 2, + "name": "unique_identifier", + "query": "host", + "skipUrlSync": true, + "type": "constant" + }, + { + "allowCustomValue": false, + "current": { + "text": "InfluxDB", + "value": "InfluxDB" + }, + "hide": 2, + "name": "datasource", + "options": [], + "query": "influxdb", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allowCustomValue": false, + "definition": "buckets()", + "hide": 2, + "name": "bucket", + "options": [], + "query": { + "query": "buckets()" + }, + "refresh": 1, + "regex": "", + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Device Details", + "uid": "bekds38txl1j4c", + "version": 35 +} \ No newline at end of file diff --git a/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/installer_agent_devices.json b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/installer_agent_devices.json new file mode 100644 index 0000000..9a09206 --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/installer_agent_devices.json @@ -0,0 +1,226 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 30, + "links": [], + "panels": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device detail link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + }, + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"task\" and\n r.source_app == \"InstallerAgent\" and\n r.success == \"true\" and\n r._field == \"user_data\" and\n r.name == \"sync_no_diff\"\n )\n\n |> group(columns: [\"${unique_identifier}\"])\n |> last(column: \"_value\")\n\n // Rename fields for clarity\n |> map(fn: (r) => ({\n device: r[\"${unique_identifier}\"],\n version: r._value,\n _time: r._time\n }))\n\n // Optional filter on version string\n |> filter(fn: (r) => r.version =~ /${version:regex}/)\n\n |> group()\n\n |> yield(name: \"host_versions\")\n", + "refId": "A" + } + ], + "title": "Deployments in use", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "_time": false + }, + "includeByName": {}, + "indexByName": { + "_time": 2, + "device": 0, + "version": 1 + }, + "renameByName": { + "_time": "Updated", + "device": "Device", + "version": "Deployment Version" + } + } + } + ], + "type": "table" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [ + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "definition": "from(bucket: \"${bucket}\")\n |> range(start: -360d)\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"task\" and\n r.source_app == \"InstallerAgent\" and\n r.success == \"true\" and\n r._field == \"user_data\" and\n r.name == \"sync_no_diff\"\n )\n |> group(columns: [\"${unique_identifier}\"])\n |> last(column: \"_value\")\n |> keep(columns: [\"_value\"])\n |> distinct(column: \"_value\")", + "includeAll": true, + "label": "Deployment Version", + "multi": true, + "name": "version", + "options": [], + "query": { + "query": "from(bucket: \"${bucket}\")\n |> range(start: -360d)\n |> filter(fn: (r) =>\n r._measurement == \"messages\" and\n r.category == \"task\" and\n r.source_app == \"InstallerAgent\" and\n r.success == \"true\" and\n r._field == \"user_data\" and\n r.name == \"sync_no_diff\"\n )\n |> group(columns: [\"${unique_identifier}\"])\n |> last(column: \"_value\")\n |> keep(columns: [\"_value\"])\n |> distinct(column: \"_value\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "current": { + "text": "host", + "value": "host" + }, + "description": "", + "hide": 2, + "name": "unique_identifier", + "query": "host", + "skipUrlSync": true, + "type": "constant" + }, + { + "current": { + "text": "Cameras", + "value": "Cameras" + }, + "definition": "buckets()", + "hide": 2, + "name": "bucket", + "options": [], + "query": { + "query": "buckets()" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "current": { + "text": "bekds38txl1j4c", + "value": "bekds38txl1j4c" + }, + "description": "UID of the device details dashboard", + "hide": 2, + "name": "device_details_uid", + "query": "bekds38txl1j4c", + "skipUrlSync": true, + "type": "constant" + }, + { + "allowCustomValue": false, + "current": { + "text": "InfluxDB", + "value": "InfluxDB" + }, + "hide": 2, + "name": "datasource", + "options": [], + "query": "influxdb", + "refresh": 1, + "regex": "", + "type": "datasource" + } + ] + }, + "time": { + "from": "now-7d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Installer Agent Devices", + "uid": "62c209c8-23f0-42dc-b4b7-a4000a162ae2", + "version": 4 + } \ No newline at end of file diff --git a/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/overview_of_devices.json b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/overview_of_devices.json new file mode 100644 index 0000000..e3e4979 --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/overview_of_devices.json @@ -0,0 +1,1194 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 5, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 15, + "panels": [], + "title": "Overview", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "internet latency" + }, + "properties": [ + { + "id": "unit", + "value": "s" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "host" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device detail link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Product Name" + }, + "properties": [ + { + "id": "custom.width", + "value": 305 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Device" + }, + "properties": [ + { + "id": "custom.width", + "value": 184 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "AXIS OS" + }, + "properties": [ + { + "id": "custom.width", + "value": 124 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "External IP" + }, + "properties": [ + { + "id": "custom.width", + "value": 155 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Local IPs" + }, + "properties": [ + { + "id": "custom.width", + "value": 477 + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 20, + "x": 0, + "y": 1 + }, + "id": 17, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "hide": false, + "query": "// Get cameras with reported data in the selected interval\r\nfrom(bucket: \"${bucket}\")\r\n // Filter on time\r\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\r\n // Filter using variable selectors (supports All and multi-select)\r\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\r\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\r\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\r\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\r\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\r\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\r\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\r\n // We cant flatten if the values are of different types, so we just\r\n // filter for an arbitrary value that we know exists...\r\n |> filter(fn: (r) =>\r\n r[\"_measurement\"] == \"cpu\" and\r\n r[\"cpu\"] == \"cpu-total\" and\r\n r[\"_field\"] == \"usage_idle\"\r\n )\r\n // Group by device to merge to one time series per device\r\n |> group(columns: [\"${unique_identifier}\"])\r\n // Get the last data point for each device\r\n |> last()\r\n // Ungroup to prepare a flat output\r\n |> group()\r\n // Keep only timestamp and device identifier\r\n |> keep(columns: [\"_time\", \"${unique_identifier}\"])\r\n // Rename _time to seen\r\n |> rename(columns: {_time: \"seen\"})", + "refId": "Get last time stamp" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "// Get latest external IP per camera in the selected interval\nfrom(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter using variable selectors (supports All and multi-select)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n // Filter on external IP measurement\n |> filter(fn: (r) => r[\"_measurement\"] == \"external_ip\")\n // Group by device to isolate each host\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last reported external IP\n |> last()\n // Ungroup to flatten result set\n |> group()\n // Rename the value column and keep only relevant fields\n |> rename(columns: {_value: \"external ip\"})\n |> keep(columns: [\"${unique_identifier}\", \"external ip\"])\n", + "refId": "Get external IPs" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "// Get latest local IP per camera in the selected interval\nfrom(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter using variable selectors (supports All and multi-select)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n // Filter on local IP measurement\n |> filter(fn: (r) => r[\"_measurement\"] == \"local_ip_addresses\")\n // Group by device to isolate each host\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last reported local IP\n |> last()\n // Ungroup to flatten result set\n |> group()\n // Rename the value column and keep only relevant fields\n |> rename(columns: {_value: \"local ip\"})\n |> keep(columns: [\"${unique_identifier}\", \"local ip\"])\n", + "refId": "Get internal IPs" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "// Get latest internet latency per camera in the selected interval\nfrom(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter using variable selectors (supports All and multi-select)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n // Filter on internet connectivity measurement and field\n |> filter(fn: (r) => r[\"_measurement\"] == \"internet_connectivity\")\n |> filter(fn: (r) => r[\"_field\"] == \"response_time\")\n // Group by device to isolate each host\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last reported response time\n |> last()\n // Ungroup to flatten result set\n |> group()\n // Rename the value column and keep only relevant fields\n |> rename(columns: {_value: \"internet latency\"})\n |> keep(columns: [\"${unique_identifier}\", \"internet latency\"])\n", + "refId": "Internet response time" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "// Get latest local IP per camera in the selected interval\nfrom(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter using variable selectors (supports All and multi-select)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n // Filter on a single meaurement to make the schema aligned\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n // Group by device to isolate each host\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last report\n |> last()\n // Ungroup to flatten result set\n |> group()\n // Keep only host and device model\n |> keep(columns: [\"${unique_identifier}\", \"product_full_name\"])\n |> rename(columns: {\n product_full_name: \"product full name\"\n })", + "refId": "Get camera model name" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n |> group(columns: [\"${unique_identifier}\"])\n |> last()\n |> group()\n |> keep(columns: [\"${unique_identifier}\", \"firmware_version\"])\n |> rename(columns: {\n \"${unique_identifier}\": \"host\",\n firmware_version: \"firmware version\"\n })\n", + "refId": "A" + } + ], + "title": "Last Report", + "transformations": [ + { + "id": "joinByField", + "options": { + "byField": "host", + "mode": "outer" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": { + "external ip": 3, + "firmware version": 2, + "host": 0, + "local ip": 4, + "product full name": 1, + "seen": 5 + }, + "renameByName": { + "external ip": "External IP", + "firmware version": "AXIS OS", + "host": "Device", + "local ip": "Local IPs", + "product full name": "Product Name", + "seen": "Last Seen" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "AXIS OS Changes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "axisPlacement": "auto", + "fillOpacity": 70, + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineWidth": 0, + "spanNulls": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 20, + "x": 0, + "y": 10 + }, + "id": 18, + "options": { + "alignValue": "left", + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "mergeValues": true, + "rowHeight": 0.9, + "showValue": "auto", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // Generic measurement just to access the tags. To reduce number of points,\n // we also filter to one single core and field.\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"cpu\" and\n r[\"cpu\"] == \"cpu-total\" and\n r[\"_field\"] == \"usage_idle\" and\n\n // Only show the ones that are reporting firmware_version\n exists r[\"firmware_version\"] and\n\n // Applied filters\n (r[\"area\"] =~ /${area:regex}/) and\n (r[\"geography\"] =~ /${geography:regex}/) and\n (r[\"region\"] =~ /${region:regex}/) and\n (r[\"site\"] =~ /${site:regex}/) and\n (r[\"type\"] =~ /${type:regex}/) and\n (r[\"${unique_identifier}\"] =~ /${device:regex}/) and\n (r[\"product_full_name\"] =~ /${model:regex}/)\n )\n\n // Aggregate into time series windows to reduce number of returned data points\n |> aggregateWindow(every: 1h, fn: last, createEmpty: false)\n\n // Keep timestamp, device and firmware_version\n |> keep(columns: [\"_time\", \"${unique_identifier}\", \"firmware_version\"])\n\n // Rename and remap to Grafana-friendly _time, _field, _value,\n // that gets rid of the \"firmware_version \" prefix in the visualization.\n |> map(fn: (r) => ({\n _time: r._time,\n _field: r[\"${unique_identifier}\"],\n _value: r[\"firmware_version\"]\n }))\n\n // Group per device (now in _field)\n |> group(columns: [\"_field\"])\n\n // Sort by time\n |> sort(columns: [\"_time\"])", + "refId": "A" + } + ], + "title": "AXIS OS Changes", + "type": "state-timeline" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "cpu" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "region 2" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "site 2" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "host" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device detail link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + }, + { + "id": "custom.width", + "value": 192 + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 20, + "x": 0, + "y": 19 + }, + "id": 12, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 13, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Trend #cpu" + } + ] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "from(bucket: \"${bucket}\")\n // Selected time span\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter based on drop-downs\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n // Filter out the fields we are interested in\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n |> filter(fn: (r) => r[\"cpu\"] == \"cpu-total\")\n |> filter(fn: (r) => r[\"_field\"] == \"usage_idle\")\n // One time series for each device\n |> group(columns: [\"${unique_identifier}\"])\n // Invert to CPU load instead of idle\n |> map(fn: (r) => ({ r with _value: 100.0 - r._value }))\n // Aggregate in blocks to reduce number of data points\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)", + "refId": "cpu" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "from(bucket: \"${bucket}\")\n // Selected time span\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter based on drop-downs\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n // Filter out the fields we are interested in\n |> filter(fn: (r) => r[\"_measurement\"] == \"mem\")\n |> filter(fn: (r) => r[\"_field\"] == \"used_percent\")\n // One time series for each device\n |> group(columns: [\"${unique_identifier}\"])\n // Aggregate in blocks to reduce number of data points\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)", + "refId": "ram" + } + ], + "title": "CPU and RAM trends", + "transformations": [ + { + "id": "timeSeriesTable", + "options": { + "cpu": { + "timeField": "Time" + }, + "ram": { + "timeField": "Time" + } + } + }, + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": {}, + "renameByName": { + "Trend #cpu": "CPU Usage", + "Trend #ram": "RAM Usage", + "host": "Device" + } + } + } + ], + "type": "table" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 3, + "panels": [], + "repeat": "device", + "title": "${device}", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red" + }, + { + "color": "dark-green", + "value": 604800 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 0, + "y": 29 + }, + "id": 5, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "groupBy": [ + { + "params": ["$__interval"], + "type": "time" + }, + { + "params": ["null"], + "type": "fill" + } + ], + "orderByTime": "ASC", + "policy": "default", + "query": "from(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter on the right device. Since this visual will be repeated, the\n // device variable will always contain the right value and only one,\n // so we do not need to do regexp here..\n |> filter(fn: (r) => r[\"${unique_identifier}\"] == \"${device}\")\n // Filter on the field we want\n |> filter(fn: (r) => r[\"_measurement\"] == \"system\")\n |> filter(fn: (r) => r[\"_field\"] == \"uptime\")\n // Group by device to merge multiple tagsets\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last sample\n |> last()", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": ["value"], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "title": "Uptime", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "fieldMinMax": false, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "#EAB839", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 3, + "y": 29 + }, + "id": 1, + "maxPerRow": 12, + "options": { + "displayMode": "gradient", + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "limit": 1, + "values": true + }, + "showUnfilled": true, + "sizing": "auto", + "text": {}, + "valueMode": "color" + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "from(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter on the right device. Since this visual will be repeated,\n // the device variable will always contain the right value and only one,\n // so we do not need to do regexp here..\n |> filter(fn: (r) => r[\"${unique_identifier}\"] == \"${device}\")\n // Filter on the measurement and field we want\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n |> filter(fn: (r) => r[\"cpu\"] == \"cpu-total\")\n |> filter(fn: (r) => r[\"_field\"] == \"usage_active\")\n // Group by device to merge multiple tagsets\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last sample\n |> last()\n", + "refId": "A" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"system\")\n |> filter(fn: (r) => r[\"_field\"] == \"n_cpus\")\n |> filter(fn: (r) => r[\"host\"] == \"${device}\")\n|> last()", + "refId": "B" + } + ], + "title": "CPU", + "type": "bargauge" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "fieldMinMax": false, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "yellow" + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 6, + "y": 29 + }, + "id": 2, + "maxPerRow": 12, + "options": { + "displayMode": "gradient", + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "maxVizHeight": 25, + "minVizHeight": 12, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "limit": 1, + "values": true + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "color" + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "from(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter on the right device. Since this visual will be repeated,\n // the device variable will always contain the right value and only one,\n // so we do not need to do regexp here..\n |> filter(fn: (r) => r[\"${unique_identifier}\"] == \"${device}\")\n // Filter on the measurement and field we want\n |> filter(fn: (r) => r[\"_measurement\"] == \"mem\")\n |> filter(fn: (r) => r[\"_field\"] == \"used_percent\")\n // Group by device to merge multiple tagsets\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last sample\n |> last()\n", + "refId": "A" + } + ], + "title": "RAM", + "type": "bargauge" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "fieldMinMax": false, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "blue" + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 9, + "y": 29 + }, + "id": 6, + "options": { + "displayMode": "gradient", + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "maxVizHeight": 25, + "minVizHeight": 12, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "limit": 1, + "values": true + }, + "showUnfilled": true, + "sizing": "auto", + "text": { + "titleSize": 1 + }, + "valueMode": "color" + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "from(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter on the right device. Since this visual will be repeated,\n // the device variable will always contain the right value and only one,\n // so we do not need to do regexp here..\n |> filter(fn: (r) => r[\"${unique_identifier}\"] == \"${device}\")\n // Filter on the measurement, field, and specific path we want\n |> filter(fn: (r) => r[\"_measurement\"] == \"disk\")\n |> filter(fn: (r) => r[\"_field\"] == \"used_percent\")\n |> filter(fn: (r) => r[\"path\"] == \"/usr/local\")\n // Group by device to merge multiple tagsets\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last sample\n |> last()\n", + "refId": "A" + } + ], + "title": "Storage", + "type": "bargauge" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "fieldMinMax": false, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "blue" + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 3, + "x": 12, + "y": 29 + }, + "id": 7, + "options": { + "displayMode": "gradient", + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "maxVizHeight": 25, + "minVizHeight": 12, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "limit": 1, + "values": true + }, + "showUnfilled": true, + "sizing": "auto", + "text": { + "titleSize": 1 + }, + "valueMode": "color" + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "query": "from(bucket: \"${bucket}\")\n // Filter on time\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n // Filter on the right device. Since this visual will be repeated,\n // the device variable will always contain the right value and only one,\n // so we do not need to do regexp here..\n |> filter(fn: (r) => r[\"${unique_identifier}\"] == \"${device}\")\n // Filter on the measurement, field, and specific path we want\n |> filter(fn: (r) => r[\"_measurement\"] == \"disk\")\n |> filter(fn: (r) => r[\"_field\"] == \"used_percent\")\n |> filter(fn: (r) => r[\"path\"] == \"/var/spool/storage/SD_DISK\")\n // Group by device to merge multiple tagsets\n |> group(columns: [\"${unique_identifier}\"])\n // Get the last sample\n |> last()\n", + "refId": "A" + } + ], + "title": "SD Card", + "type": "bargauge" + } + ], + "preload": false, + "refresh": "15m", + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [ + { + "allowCustomValue": true, + "current": { + "text": "InfluxDB", + "value": "InfluxDB" + }, + "description": "", + "hide": 2, + "label": "Data Source", + "name": "datasource", + "options": [], + "query": "influxdb", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "buckets()", + "hide": 2, + "label": "Database", + "name": "bucket", + "options": [], + "query": { + "query": "buckets()" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"${unique_identifier}\")", + "description": "All devices that are known in the schema", + "includeAll": true, + "label": "Device", + "multi": true, + "name": "device", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"${unique_identifier}\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"geography\")", + "includeAll": true, + "label": "Geography", + "multi": true, + "name": "geography", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"geography\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"region\")", + "description": "", + "includeAll": true, + "label": "Region", + "multi": true, + "name": "region", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"region\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"area\")", + "description": "", + "includeAll": true, + "label": "Area", + "multi": true, + "name": "area", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"area\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"site\")", + "description": "", + "includeAll": true, + "label": "Site", + "multi": true, + "name": "site", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"site\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": ["All"], + "value": ["$__all"] + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"type\")", + "includeAll": true, + "label": "Type", + "multi": true, + "name": "type", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"type\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "current": { + "text": "host", + "value": "host" + }, + "description": "The name of the field for the unique identifier like the serial number", + "hide": 2, + "name": "unique_identifier", + "query": "host", + "skipUrlSync": true, + "type": "constant" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"product_full_name\")", + "description": "Model of the camera", + "includeAll": true, + "label": "Model Name", + "multi": true, + "name": "model", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"product_full_name\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "current": { + "text": "bekds38txl1j4c", + "value": "bekds38txl1j4c" + }, + "description": "UID of the device details dashboard", + "hide": 2, + "name": "device_details_uid", + "query": "bekds38txl1j4c", + "skipUrlSync": true, + "type": "constant" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Overview of Devices", + "uid": "ceh7yyrnarj7kd", + "version": 29 +} \ No newline at end of file diff --git a/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/system_overview.json b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/system_overview.json new file mode 100644 index 0000000..88662e8 --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/provisioning/dashboards/system_overview.json @@ -0,0 +1,1150 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 7, + "links": [], + "panels": [ + { + "datasource": { + "uid": "${datasource}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 4, + "x": 0, + "y": 0 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n |> filter(fn: (r) => r[\"cpu\"] == \"cpu-total\")\n |> keep(columns: [\"host\"])\n |> group()\n |> distinct(column: \"host\")\n |> count(column: \"_value\")\n |> rename(columns: {_value: \"total\"})\n |> yield(name: \"all\")\n\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n |> filter(fn: (r) => r[\"_measurement\"] == \"cpu\")\n |> filter(fn: (r) => r[\"cpu\"] == \"cpu-total\")\n |> keep(columns: [\"host\"])\n |> group()\n |> distinct(column: \"host\")\n |> count(column: \"_value\")\n |> rename(columns: {_value: \"selected\"})\n |> yield(name: \"filtered\")", + "refId": "A" + } + ], + "title": "Device Count", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "Known devices that does not have any data during this time span.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "seen" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "known" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "in_interval" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "seen_earlier" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "dummy" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "host" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device detail link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}&from=now-90d" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 4, + "y": 0 + }, + "id": 12, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 1, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Trend #cpu" + } + ] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "import \"array\"\n\n// Get cameras with reported data in the selected interval\nreal_data = from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"cpu\" and\n r[\"cpu\"] == \"cpu-total\" and\n r[\"_field\"] == \"usage_idle\" and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n |> group(columns: [\"host\"])\n |> last()\n |> group()\n |> keep(columns: [\"_time\", \"host\"])\n |> rename(columns: {_time: \"seen\"})\n |> map(fn: (r) => ({ r with in_interval: true })) // Add extra field for easier identification\n\n// Dummy fallback row (ensures structure exists even if real data is empty)\nfake_row = array.from(rows: [\n {\n host: \"dummy_device\",\n seen: time(v: 0),\n in_interval: true,\n dummy: 1\n }\n])\n\n// Combine real data with dummy row\nunion(tables: [real_data, fake_row])", + "refId": "in-interval" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "import \"influxdata/influxdb/schema\"\n\n// Step 1: Get all known hosts\nhosts = schema.tagValues(\n bucket: \"${bucket}\",\n tag: \"host\"\n)\n |> rename(columns: {_value: \"host\"})\n |> map(fn: (r) => ({ r with known: true }))\n\n// Step 2: Get real data to apply tag-based filters\nfiltered_hosts = from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"cpu\" and\n r[\"cpu\"] == \"cpu-total\" and\n r[\"_field\"] == \"usage_idle\" and\n (exists r[\"area\"] and r[\"area\"] =~ /${area:regex}/) and\n (exists r[\"geography\"] and r[\"geography\"] =~ /${geography:regex}/) and\n (exists r[\"region\"] and r[\"region\"] =~ /${region:regex}/) and\n (exists r[\"site\"] and r[\"site\"] =~ /${site:regex}/) and\n (exists r[\"type\"] and r[\"type\"] =~ /${type:regex}/) and\n (exists r[\"${unique_identifier}\"] and r[\"${unique_identifier}\"] =~ /${device:regex}/) and\n (exists r[\"product_full_name\"] and r[\"product_full_name\"] =~ /${model:regex}/)\n )\n |> keep(columns: [\"host\"])\n |> group()\n |> distinct(column: \"host\")\n\n// Step 3: Join to retain only filtered hosts from schema list\njoin(\n tables: {all: hosts, filtered: filtered_hosts},\n on: [\"host\"]\n)\n", + "refId": "all_devices" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "// Get last timestamp from a host (check the last 3 months back)\nfrom(bucket: \"${bucket}\")\n |> range(start: -90d)\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"cpu\" and\n r[\"cpu\"] == \"cpu-total\" and\n r[\"_field\"] == \"usage_idle\" and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"type\"] =~ /${type:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/\n )\n |> group(columns: [\"host\"])\n |> last()\n |> group()\n |> keep(columns: [\"_time\", \"host\"])\n |> rename(columns: {_time: \"last seen\"})\n |> map(fn: (r) => ({ r with seen_earlier: true })) // Add extra field for easier identification", + "refId": "last-seen" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": true, + "query": "// NOTE: This does not work as expected...\nfrom(bucket: \"${bucket}\")\n |> range(start: -90d)\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"cpu\" and\n r[\"cpu\"] == \"cpu-total\" and\n r[\"_field\"] == \"usage_idle\"\n )\n |> group(columns: [\"host\"])\n |> last()\n |> group()\n |> keep(columns: [\"host\", \"area\", \"geography\", \"region\", \"site\", \"type\", \"${unique_identifier}\", \"product_full_name\"])\n |> map(fn: (r) => ({\n host: r.host,\n filter_applies:\n (\n (r[\"area\"] =~ /${area:regex}/) and\n (r[\"geography\"] =~ /${geography:regex}/) and\n (r[\"region\"] =~ /${region:regex}/) and\n (r[\"site\"] =~ /${site:regex}/) and\n (r[\"type\"] =~ /${type:regex}/) and\n (r[\"${unique_identifier}\"] =~ /${device:regex}/) and\n (r[\"product_full_name\"] =~ /${model:regex}/)\n )\n }))\n", + "refId": "filter applies" + } + ], + "title": "Missing Devices", + "transformations": [ + { + "id": "joinByField", + "options": { + "byField": "host", + "mode": "outer" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "isNull", + "options": {} + }, + "fieldName": "in_interval" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "equal", + "options": { + "value": "dummy_device" + } + }, + "fieldName": "host" + }, + { + "config": { + "id": "isNull", + "options": {} + }, + "fieldName": "filter_applies" + } + ], + "match": "any", + "type": "exclude" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": {}, + "renameByName": { + "host": "Device", + "last seen": "Last Seen" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "New devices that did not exist before the start of the selected time span.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "seen" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "known" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "in_interval" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "seen_earlier" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "_time" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "dummy" + }, + "properties": [ + { + "id": "custom.hidden", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "host" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device details link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 12, + "y": 0 + }, + "id": 17, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 1, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Trend #cpu" + } + ] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "// Get cameras with reported data in the selected interval\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => (\n r[\"_measurement\"] == \"cpu\" and r[\"cpu\"] == \"cpu-total\" and r[\"_field\"] == \"usage_idle\"\n ))\n |> group(columns: [\"host\"])\n |> first()\n |> group()\n |> keep(columns: [\"_time\", \"host\"])\n |> rename(columns: {_time: \"first seen\"})\n |> map(fn: (r) => ({ r with in_interval: true })) // Add extra field for easier identification", + "refId": "in-interval" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "import \"array\"\nimport \"experimental\"\n\n// Make a query the last year before the selected interval starts,\n// that way we can find devices that was now known before the start of this\n// interval.\nbefore_data = from(bucket: \"${bucket}\")\n |> range(start: experimental.subDuration(from: v.timeRangeStart, d: 365d), stop: v.timeRangeStart) // Before the interval starts\n |> filter(fn: (r) => (\n r[\"_measurement\"] == \"cpu\" and r[\"cpu\"] == \"cpu-total\" and r[\"_field\"] == \"usage_idle\"\n ))\n |> group(columns: [\"host\"])\n |> last()\n |> group()\n |> keep(columns: [\"_time\", \"host\"])\n |> map(fn: (r) => ({ r with seen_earlier: true })) // Add extra field for easier identification\n\n// Dummy fallback row to ensure structure\nbefore_dummy =\n array.from(rows: [{\n host: \"dummy_device\",\n _time: time(v: 0),\n seen_earlier: true,\n dummy: 1\n }])\n\n// Combine real before data with fallback\nunion(tables: [before_data, before_dummy])", + "refId": "before-interval" + } + ], + "title": "New Devices", + "transformations": [ + { + "id": "joinByField", + "options": { + "byField": "host", + "mode": "outer" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "isNull", + "options": {} + }, + "fieldName": "seen_earlier" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": {}, + "renameByName": { + "first seen": "First Seen", + "host": "Device" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "Top k:\n* Highest load1\n* Highest load15\n* Shortest uptime\n* Highest memory used\n* Most flash storage (built in) used\n* Most SD card storage used", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "uptime" + }, + "properties": [ + { + "id": "unit", + "value": "s" + }, + { + "id": "decimals", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "memory used" + }, + "properties": [ + { + "id": "unit", + "value": "percent" + }, + { + "id": "decimals", + "value": 0 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "flash used" + }, + "properties": [ + { + "id": "unit", + "value": "percent" + }, + { + "id": "decimals", + "value": 0 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "sd card used" + }, + "properties": [ + { + "id": "unit", + "value": "percent" + }, + { + "id": "decimals", + "value": 0 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "host" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Device Detail Link", + "url": "/d/${device_details_uid:raw}/device-details?var-device=${__value.text}" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 20, + "x": 0, + "y": 11 + }, + "id": 13, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "host" + } + ] + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "metric_to_use = \"load1\"\n\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1. Apply metadata filters first (only if fields exist)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n\n // 2. Then filter for the metric of interest\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"system\" and\n r[\"_field\"] == metric_to_use\n )\n\n // 3. Aggregate, format, and limit\n |> group(columns: [\"host\"])\n |> mean()\n |> group()\n |> rename(columns: {_value: metric_to_use})\n |> keep(columns: [\"host\", metric_to_use])\n |> sort(columns: [metric_to_use], desc: true)\n |> limit(n: ${outlier_count})\n", + "refId": "load1" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "metric_to_use = \"load15\"\n\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1. Metadata filters (only if fields exist)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n\n // 2. Metric filtering\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"system\" and\n r[\"_field\"] == metric_to_use\n )\n\n // 3. Aggregation and formatting\n |> group(columns: [\"host\"])\n |> mean()\n |> group()\n |> rename(columns: {_value: metric_to_use})\n |> keep(columns: [\"host\", metric_to_use])\n\n // 4. Sort and limit\n |> sort(columns: [metric_to_use], desc: true)\n |> limit(n: ${outlier_count})\n", + "refId": "load15" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "metric_to_use = \"uptime\"\n\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1. Metadata filters (only apply if fields exist)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n\n // 2. Metric filter for uptime\n |> filter(fn: (r) => r[\"_measurement\"] == \"system\" and r[\"_field\"] == metric_to_use)\n\n // 3. Aggregate and format\n |> group(columns: [\"host\"])\n |> last() // Last reported uptime during the selected time span\n |> group()\n |> rename(columns: {_value: metric_to_use})\n |> keep(columns: [\"host\", metric_to_use])\n |> sort(columns: [metric_to_use], desc: false) // Ascending = lowest uptime\n |> limit(n: ${outlier_count})\n", + "refId": "uptime" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1. Metadata filters (safe if tag missing)\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n\n // 2. Metric filter\n |> filter(fn: (r) => r[\"_measurement\"] == \"mem\" and r[\"_field\"] == \"used_percent\")\n\n // 3. Aggregation and formatting\n |> group(columns: [\"host\"])\n |> mean()\n |> group()\n |> rename(columns: {_value: \"memory used\"})\n |> keep(columns: [\"host\", \"memory used\"])\n\n // 4. Sort by memory used (descending = highest usage)\n |> sort(columns: [\"memory used\"], desc: true)\n |> limit(n: ${outlier_count})\n", + "refId": "memory" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "metric_name = \"flash used\"\ndisk_path = \"/usr/local\"\n\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1. Metadata filters\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n\n // 2. Metric and path filter\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"disk\" and\n r[\"_field\"] == \"used_percent\" and\n r[\"path\"] == disk_path\n )\n\n // 3. Aggregate and format\n |> group(columns: [\"host\"])\n |> mean() // Mean over the selected time span\n |> group()\n |> rename(columns: {_value: metric_name})\n |> keep(columns: [\"host\", metric_name])\n\n // 4. Sort and limit\n |> sort(columns: [metric_name], desc: true)\n |> limit(n: ${outlier_count})\n", + "refId": "flash" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "hide": false, + "query": "metric_name = \"sd card used\"\ndisk_path = \"/var/spool/storage/SD_DISK\"\n\nfrom(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n\n // 1. Metadata filters\n |> filter(fn: (r) => r[\"area\"] =~ /${area:regex}/)\n |> filter(fn: (r) => r[\"geography\"] =~ /${geography:regex}/)\n |> filter(fn: (r) => r[\"region\"] =~ /${region:regex}/)\n |> filter(fn: (r) => r[\"site\"] =~ /${site:regex}/)\n |> filter(fn: (r) => r[\"type\"] =~ /${type:regex}/)\n |> filter(fn: (r) => r[\"${unique_identifier}\"] =~ /${device:regex}/)\n |> filter(fn: (r) => r[\"product_full_name\"] =~ /${model:regex}/)\n\n // 2. Metric and path filter\n |> filter(fn: (r) =>\n r[\"_measurement\"] == \"disk\" and\n r[\"_field\"] == \"used_percent\" and\n r[\"path\"] == disk_path\n )\n\n // 3. Aggregate and format\n |> group(columns: [\"host\"])\n |> mean() // Mean over the selected time span\n |> group()\n |> rename(columns: {_value: metric_name})\n |> keep(columns: [\"host\", metric_name])\n\n // 4. Sort and limit\n |> sort(columns: [metric_name], desc: true)\n |> limit(n: ${outlier_count})\n", + "refId": "SD card" + } + ], + "title": "Outliers", + "transformations": [ + { + "id": "joinByField", + "options": { + "byField": "host", + "mode": "outer" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": {}, + "renameByName": { + "flash used": "Flash Used", + "host": "Device", + "load1": "Load 1 minute", + "load15": "Load 15 minutes", + "memory used": "RAM Used", + "sd card used": "SD Card Used", + "uptime": "Uptime" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 20, + "x": 0, + "y": 19 + }, + "id": 18, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + }, + "xField": "type", + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "query": "cpu = from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"cpu\" and\n r._field == \"usage_idle\" and\n r.cpu == \"cpu-total\" and\n exists r.type and\n r.type =~ /${type:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/\n )\n |> map(fn: (r) => ({ r with _value: 100.0 - r._value }))\n |> group(columns: [\"type\"])\n |> mean()\n |> map(fn: (r) => ({ r with metric: \"CPU Usage\" }))\n\nram = from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"mem\" and\n r._field == \"used_percent\" and\n exists r.type and\n r.type =~ /${type:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/\n )\n |> group(columns: [\"type\"])\n |> mean()\n |> map(fn: (r) => ({ r with metric: \"RAM Usage\" }))\n\ndisk_usr_local = from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"disk\" and\n r._field == \"used_percent\" and\n r.path == \"/usr/local\" and\n exists r.type and\n r.type =~ /${type:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/\n )\n |> group(columns: [\"type\"])\n |> mean()\n |> map(fn: (r) => ({ r with metric: \"Disk /usr/local\" }))\n\ndisk_sd = from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) =>\n r._measurement == \"disk\" and\n r._field == \"used_percent\" and\n r.path == \"/var/volatile/spool/storage/SD_DISK\" and\n exists r.type and\n r.type =~ /${type:regex}/ and\n r[\"area\"] =~ /${area:regex}/ and\n r[\"geography\"] =~ /${geography:regex}/ and\n r[\"region\"] =~ /${region:regex}/ and\n r[\"site\"] =~ /${site:regex}/ and\n r[\"product_full_name\"] =~ /${model:regex}/ and\n r[\"${unique_identifier}\"] =~ /${device:regex}/\n )\n |> group(columns: [\"type\"])\n |> mean()\n |> map(fn: (r) => ({ r with metric: \"Disk /SD_DISK\" }))\n\nunion(tables: [cpu, ram, disk_usr_local, disk_sd])\n |> group()\n |> pivot(rowKey: [\"type\"], columnKey: [\"metric\"], valueColumn: \"_value\")\n", + "refId": "A" + } + ], + "title": "Average CPU / RAM / Disk Usage per Type", + "type": "barchart" + } + ], + "preload": false, + "refresh": "15m", + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [ + { + "allowCustomValue": false, + "current": { + "text": "InfluxDB", + "value": "InfluxDB" + }, + "description": "", + "hide": 2, + "label": "Data Source", + "name": "datasource", + "options": [], + "query": "influxdb", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allowCustomValue": false, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "buckets()", + "hide": 2, + "label": "Database", + "name": "bucket", + "options": [], + "query": { + "query": "buckets()" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"geography\")", + "includeAll": true, + "label": "Geography", + "multi": true, + "name": "geography", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"geography\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"region\")", + "description": "", + "includeAll": true, + "label": "Region", + "multi": true, + "name": "region", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"region\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"area\")", + "description": "", + "includeAll": true, + "label": "Area", + "multi": true, + "name": "area", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"area\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"site\")", + "description": "", + "includeAll": true, + "label": "Site", + "multi": true, + "name": "site", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"site\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"type\")", + "includeAll": true, + "label": "Type", + "multi": true, + "name": "type", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"type\")" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"${unique_identifier}\")", + "description": "", + "includeAll": true, + "label": "Device", + "multi": true, + "name": "device", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"${unique_identifier}\")" + }, + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + }, + { + "current": { + "text": "host", + "value": "host" + }, + "description": "The name of the field for the unique identifier like the serial number", + "hide": 2, + "name": "unique_identifier", + "query": "host", + "skipUrlSync": true, + "type": "constant" + }, + { + "allValue": ".*", + "allowCustomValue": false, + "current": { + "text": "All", + "value": ["$__all"] + }, + "datasource": { + "type": "influxdb", + "uid": "${datasource}" + }, + "definition": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"product_full_name\")", + "includeAll": true, + "label": "Model Name", + "multi": true, + "name": "model", + "options": [], + "query": { + "query": "import \"influxdata/influxdb/schema\"\n\nschema.tagValues(bucket: \"${bucket}\", tag: \"product_full_name\")" + }, + "refresh": 1, + "regex": "", + "sort": 1, + "type": "query" + }, + { + "current": { + "text": "5", + "value": "5" + }, + "label": "K-Outliers", + "name": "outlier_count", + "options": [ + { + "selected": false, + "text": "1", + "value": "1" + }, + { + "selected": false, + "text": "2", + "value": "2" + }, + { + "selected": false, + "text": "3", + "value": "3" + }, + { + "selected": false, + "text": "4", + "value": "4" + }, + { + "selected": true, + "text": "5", + "value": "5" + }, + { + "selected": false, + "text": "10", + "value": "10" + }, + { + "selected": false, + "text": "50", + "value": "50" + } + ], + "query": "1,2,3,4,5,10,50", + "type": "custom" + }, + { + "current": { + "text": "bekds38txl1j4c", + "value": "bekds38txl1j4c" + }, + "description": "UID of the device details dashboard", + "hide": 2, + "name": "device_details_uid", + "query": "bekds38txl1j4c", + "skipUrlSync": true, + "type": "constant" + } + ] + }, + "time": { + "from": "now-7d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "System Overview", + "uid": "dsdsadfg3t3t3", + "version": 27 +} \ No newline at end of file diff --git a/deployment-and-dashboards-influxdb-v2/provisioning/datasources/datasource.yaml b/deployment-and-dashboards-influxdb-v2/provisioning/datasources/datasource.yaml new file mode 100644 index 0000000..0b7c79b --- /dev/null +++ b/deployment-and-dashboards-influxdb-v2/provisioning/datasources/datasource.yaml @@ -0,0 +1,15 @@ +apiVersion: 1 + +datasources: + - name: InfluxDB + type: influxdb + access: proxy + url: http://influxdb:8086 + jsonData: + version: Flux + organization: ${INFLUXDB_ORG} + defaultBucket: ${INFLUXDB_BUCKET} + timeInterval: "15s" + secureJsonData: + token: ${INFLUXDB_TOKEN} + isDefault: true