Overview
+ +LibreNMS is a great network device monitoring system, and Grafana is a w= +onderful tool for visualising and alerting on data from multiple sources. +Both are great examples of open-source software that you should be consider= +ing for your environment.
+ +However, while LibreNMS does provide official integration with many external d=
+atabases that you can then use as a data source in Grafana, if you don=E2=
+=80=99t already have one of these in your environment it requires you to de=
+ploy these as well.
+I=E2=80=99ve also personally run into performance issues with the Prometheus Pu=
+shgateway integration when pushing a large number of metrics, probably =
+because Pushgatew=
+ay isn=E2=80=99t designed for that use case.
Recently I stumbled upon a Reddit post=
+ by user /u/wjhinz which described a method for =E2=80=9C(almost) nativ=
+ely=E2=80=9D using LibreNMS as a data source in Grafana by using RRDReST as an API endpoint to acces=
+s the RRD files that LibreNMS creates.
+After testing this and confirming it works very well, I wanted to add to th=
+e Reddit post by documenting what a containerised deployment of this soluti=
+on looks like.
Major kudos to both /u/tb= +otnz (for their work on RRDReST) and /u/wjhinz (for documenting this solution).
+ +Containerised Deployment
+ +LibreNMS
+ +I=E2=80=99ll be using Docker Compose for this deployment, and starting w=
+ith the LibreNMS docker example compose file as the=
+ base.
+I=E2=80=99ve trimmed some services that aren=E2=80=99t needed (mstpd, syslo=
+g and SNMP traps), and included my compose file in an appendix section.
RRDReST
+ +Next, we want to add an RRDReST container. As a container image doesn=E2=
+=80=99t currently exist, we will need to create one.
+Thanks to tbotnz, who has provided instructions on how to get RRDReST running in his Git=
+hub repository, creating a container image is relatively easy.
+Here=E2=80=99s the Dockerfile I=E2=80=99ve written:
FROM python:3.10-alpine
+RUN apk add g++ make rrdtool
+COPY requirements.txt /opt/RRDRe=
+ST/requirements.txt
+WORKDIR /opt/RRDReST
+RUN pip3 install -r requirements.txt
+COPY . /opt/RRDReST
+ENTRYPOINT ["uvicorn", "rrdrest:=
+rrd_rest"]
+CMD ["--host", "0.0.0.0", "--por=
+t", "9000"]
+
I=E2=80=99ve built the container using this and pushed it to be pulled from Docker Hub<= +/a> as well.
+ +To add this to our deployment, we simply need to add another service to =
+the docker-compose.yml=
+
file and give the container access to the LibreNMS volume (so it ca=
+n read the RRD files).
+I=E2=80=99m using /opt=
+/librenms
as the mount point for the volume, as it mimics where data=
+ would be located in a non-containerised deployment.
services:
+ ...
+ rrdrest:
+ image: michaelwadman/rrdrest:latest
+ container_name: rrdrest
+ volumes:
+ - "./librenms:/opt/librenms"
+
As /u/wjhinz notes in their post, RRDReST needs read access to the RRD f= +iles, which should be fine with a docker-compose set up like this, but coul= +d be a good place to start investigations if it isn=E2=80=99t working for y= +ou.
+ +Grafana
+ +Finally, we want to add a Grafana container.
+ +services:
+ ...
+ grafana:
+ image: grafana/grafana-oss:latest
+ container_name: grafana
+ ports:
+ - target: 3000
+ published: 3000
+ protocol: tcp
+ environment:
+ - "GF_INSTALL_PLUGINS=3Dmarcusolsson-json-datasource"
+
Note that I=E2=80=99m adding an environment variable for the installatio= +n of the JSON API plugin, as this is needed to interact with both th= +e LibreNMS API as well as the RRDReST API.
+ +docker-compose up
+ +With a complete compose file, we can spin up the environment:
+ +$ =
+sudo docker-compose up -d
+Creating network "librenms_grafana_default" with =
+the default driver
+Creating librenms ... done
+Creating grafana ... done
+Creating librenms_db ... done
+Creating librenms_dispatcher ... done
+Creating librenms_redis ... done
+Creating librenms_memcached ... done
+Creating rrdrest ... done
+
Adding data sources in Grafana
+
+Creating LibreNMS user
+
+
To add LibreNMS as a data source in Grafana, we first need to create an =
+API token in LibreNMS.
+Visit the LibreNMS web interface and log in (in my case, =E2=80=9Chttp://12=
+7.0.0.1:8000=E2=80=9D, with the default credentials of librenms/librenms).<=
+/p>
+
+
Because Grafana will only require read access, we=E2=80=99re going to cr=
+eate a new user with read-only access and then associate the token we creat=
+e with that user.
+Under =E2=80=98Settings -> Manage Users=E2=80=99, I created a user named=
+ =E2=80=9Cgrafana=E2=80=9D, changed the access level to =E2=80=9CGlobal Rea=
+d=E2=80=9D and set a random password as we will only be using the API acces=
+s.
Then, under =E2=80=98Settings -> API -> API Settings=E2=80=99, I c= +reated an API token for the grafana user:
+ +Make sure to copy the token when creating it, as we=E2=80=99ll need that= + later when adding the data source into LibreNMS.
+ +Adding LibreNMS data sour= +ce in Grafana
+ +After logging into the Grafana web interface (in my case, =E2=80=9Chttp:=
+//127.0.0.1:3000=E2=80=9D, with the default credentials of admin/admin), na=
+vigate to =E2=80=98Configuration -> Data Sources=E2=80=99 from the left =
+pane.
+Add a new data source and select the JSON API type from the list.
In the settings for the LibreNMS API data source, we need to change the =
+URL and add the token authentication header.
+The URL should be set to your LibreNMS host and port, with the API endpoint=
+ of =E2=80=9C/api/v0=E2=80=9D appended.
+Then, under =E2=80=9CCustom HTTP Headers=E2=80=9D, select =E2=80=9CAdd Head=
+er=E2=80=9D. The header name will be =E2=80=9CX-Auth-Token=E2=80=9D and the=
+ value will be the token string that you created above.
+Save and test that the API endpoint is working for you, and you should get =
+a green =E2=80=9CSuccess=E2=80=9D pop-up if everything is working.
My data source looked like the following when finished:
+ +Adding RRDReST data source= + in Grafana
+ +Like the above, we=E2=80=99re going to also add the RRDReST API as a new=
+ data source in Grafana too.
+This will also be a JSON API, so select this from the data source type when=
+ creating it.
In the settings for the RRDReST API data source, we only need to change =
+the URL.
+Again, point this towards your RRDReST container host and port, and leave t=
+he endpoint blank.
+Save and test that the API endpoint is working for you, and you should get =
+a red =E2=80=9CUnprocessable Entity=E2=80=9D pop-up if everything is workin=
+g. This error means the API is reachable, but the plugin can=E2=80=99t proc=
+ess the data returned (which is expected, because there isn=E2=80=99t any d=
+ata returned, as we haven=E2=80=99t pointed it to an RRD file yet).
My data source looked like the following when finished:
+ +Creating a dashboard
+ +The last thing to do is to create a dashboard for the LibreNMS data to b=
+e visualised.
+From the left pane in Grafana, select =E2=80=98Create -> Import=E2=80=99=
+ and then upload the JSON file cr=
+eated by /u/wjhinz on Pastebin. I=E2=80=99ve also included it in an app=
+endix section below in the case that the Pastebin link goes away for some r=
+eason.
After import, you=E2=80=99ll be prompted to name the dashboard, place it= + into a folder and select the data sources you created before. Here=E2=80= +=99s what mine looked like before import:
+ +There was one last item that I needed to change to get data displayed on=
+ the graph panels, which was to set the timezone to UTC. I believe this is =
+needed because LibreNMS stores (or RRDReST represents) data in UTC format, =
+but Grafana queries it with the local browser timezone by default.
+To do so, drop-down the time picker from the top right of the dashboard scr=
+een, select =E2=80=9CChange Time Settings=E2=80=9D and choose UTC.
If you=E2=80=99ve got devices in LibreNMS, they should appear up the top= + left of the page in a drop down menu along with a list of interfaces. Afte= +r selecting a device and an interface, both the =E2=80=9CBandwidth In/Out= +=E2=80=9D and =E2=80=9CErrors In/Out=E2=80=9D graphs will populate with inf= +ormation:
+ +While the panels in the dashboard provided only show port throughput and=
+ errors, it=E2=80=99s pretty easy to pick up the other RRD files in a devic=
+e directory and display those statistics as well.
+For example, LibreNMS has created 64 other RRD files full of stats for my t=
+est device:
$ =
+sudo docker exec -it rrdrest ls /opt/librenms/rrd/
+Test_Device
+$ sudo docker exec -it rrdrest ls /opt/librenms/rrd/Test_Device/ | grep -v port | wc -l
+64
+
Conclusion
+ +Providing RRDReST access to the RRD files that LibreNMS creates is not o=
+nly easy, but it performs well too.
+If you are already using Grafana in your environment to visualise, analyse =
+and/or alert on data in a centralised fashion, then pulling SNMP metrics in=
+ through LibreNMS (and its=E2=80=99 device discovery feature) from devices =
+that are otherwise unmonitored and using RRDReST as a data source is a grea=
+t =E2=80=9Cquick win=E2=80=9D.
Appendices
+ +References
+ +wjhinz=E2=80=99s Reddit Post
+RRDReST Repository
+RRDReST Containe=
+r Image
+Grafana JSON API Data source Plugin
Versions used
+ +Desktop Machine: Ubuntu 20.04
+Docker: 20.10.12
+Docker Compose: 1.26.2
+LibreNMS: 21.11.0
+Grafana: 8.3.3
Full Code
+ +Docker Compose file
+ +version: "3.5"
+
+services:
+ db:
+ image: mariadb:10.5
+ container_name: librenms_db
+ command:
+ - "mysqld"
+ - "--innodb-file-per-table=3D1"
+ - "--lower-case-table-names=3D0"
+ - "--character-set-server=3Dutf8mb4"
+ - "--collation-server=3Dutf8mb4_unicode_ci"
+ volumes:
+ - "./db:/var/lib/mysql"
+ environment:
+ - "TZ=3DPacific/Auckland"
+ - "MYSQL_ALLOW_EMPTY_PASSWORD=3Dyes"
+ - "MYSQL_DATABASE=3Dlibrenms"
+ - "MYSQL_USER=3Dlibrenms"
+ - "MYSQL_PASSWORD=3Dasupersecretpassword"
+ restart: always
+
+ memcached:
+ image: memcached:alpine
+ container_name: librenms_memcached
+ environment:
+ - "TZ=3DPacific/Auckland"
+ restart: always
+
+ redis:
+ image: redis:5.0-alpine
+ container_name: librenms_redis
+ environment:
+ - "TZ=3DPacific/Auckland"
+ restart: always
+
+ librenms:
+ image: librenms/librenms:latest
+ container_name: librenms
+ hostname: librenms
+ cap_add:
+ - NET_ADMIN
+ - NET_RAW
+ ports:
+ - target: 8000
+ published: 8000
+ protocol: tcp
+ volumes:
+ - "./librenms:/data"
+ environment:
+ - "TZ=3DPacific/Auckland"
+ - "PUID=3D1000"
+ - "PGID=3D1000"
+ - "DB_HOST=3Ddb"
+ - "DB_NAME=3Dlibrenms"
+ - "DB_USER=3Dlibrenms"
+ - "DB_PASSWORD=3Dasupersecretpassword"
+ - "DB_TIMEOUT=3D60"
+ - "REDIS_HOST=3Dredis"
+ - "REDIS_PORT=3D6379"
+ - "REDIS_DB=3D0"
+ - "MEMORY_LIMIT=3D256M"
+ - "UPLOAD_MAX_SIZE=3D16M"
+ - "OPCACHE_MEM_SIZE=3D128"
+ - "REAL_IP_FROM=3D0.0.0.0/32"
+ - "REAL_IP_HEADER=3DX-Forwarded-For"
+ - "LOG_IP_VAR=3Dremote_addr"
+ - "MEMCACHED_HOST=3Dmemcached"
+ - "MEMCACHED_PORT=3D11211"
+ - "LIBRENMS_WEATHERMAP=3Dfalse"
+ restart: always
+
+ dispatcher:
+ image: librenms/librenms:latest
+ container_name: librenms_dispatcher
+ hostname: librenms-dispatcher
+ cap_add:
+ - NET_ADMIN
+ - NET_RAW
+ volumes:
+ - "./librenms:/data"
+ environment:
+ - "TZ=3DPacific/Auckland"
+ - "PUID=3D1000"
+ - "PGID=3D1000"
+ - "DB_HOST=3Ddb"
+ - "DB_NAME=3Dlibrenms"
+ - "DB_USER=3Dlibrenms"
+ - "DB_PASSWORD=3Dasupersecretpassword"
+ - "DB_TIMEOUT=3D60"
+ - "DISPATCHER_NODE_ID=3Ddispatcher1"
+ - "REDIS_HOST=3Dredis"
+ - "REDIS_PORT=3D6379"
+ - "REDIS_DB=3D0"
+ - "SIDECAR_DISPATCHER=3D1"
+ - "MEMORY_LIMIT=3D256M"
+ - "UPLOAD_MAX_SIZE=3D16M"
+ - "OPCACHE_MEM_SIZE=3D128"
+ - "REAL_IP_FROM=3D0.0.0.0/32"
+ - "REAL_IP_HEADER=3DX-Forwarded-For"
+ - "LOG_IP_VAR=3Dremote_addr"
+ - "MEMCACHED_HOST=3Dmemcached"
+ - "MEMCACHED_PORT=3D11211"
+ - "LIBRENMS_WEATHERMAP=3Dfalse"
+ restart: always
+
+ rrdrest:
+ image: michaelwadman/rrdrest:latest
+ container_name: rrdrest
+ volumes:
+ - "./librenms:/opt/librenms"
+
+ grafana:
+ image: grafana/grafana-oss:latest
+ container_name: grafana
+ ports:
+ - target: 3000
+ published: 3000
+ protocol: tcp
+ environment:
+ - "GF_INSTALL_PLUGINS=3Dmarcusolsson-json-datasource"
+
Grafana Dashboard JSON
+ +{
+ "__inputs": [
+ {
+ "name": "DS_RRDREST_API",
+ "label": "RRDRest API",
+ "description": ""=
+,
+ "type": "datasource",
+ "pluginId":=
+ "marcusolsson-json-datasource"=
+,
+ "pluginName": "JSON API"
+ },
+ {
+ "name": "DS_LIBRENMS_API",
+ "label": "LibreNMS API",
+ "description": ""=
+,
+ "type": "datasource",
+ "pluginId":=
+ "marcusolsson-json-datasource"=
+,
+ "pluginName": "JSON API"
+ }
+ ],
+ "__requires": [
+ {
+ "type": "grafana"=
+,
+ "id": "grafana",<=
+/span>
+ "name": "Grafana"
+ },
+ {
+ "type": "panel",<=
+/span>
+ "id": "graph",
+ "name": "Graph",<=
+/span>
+ "version":<=
+span class=3D"w"> ""
+ },
+ {
+ "type": "datasource",
+ "id": "marcusolsson-json-datasource",
+ "name": "JSON API",
+ "version":<=
+span class=3D"w"> "1.2.1"
+ }
+ ],
+ "annotations":<=
+span class=3D"w"> {
+ "list": [
+ {
+ "builtIn": 1,=
+span>
+ "datasource": "-- Grafana --",
+ "enable":=
+ true,=
+
+ "hide": true,=
+span>
+ "iconColor": "rgba(0, 211, 255, 1)" ,
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip":=
+ 0,
+ "id": null,
+ "iteration": 1626152089476,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,=
+
+ "dashes": false,<=
+/span>
+ "datasource": "${DS_RRDREST_API}",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {},
+ "unit":=
+ "bps"
+ },
+ "overrides": []
+ },
+ "fill": 1,=
+
+ "fillGradient": 0=
+,
+ "gridPos":<=
+span class=3D"w"> {
+ "h": 15,=
+
+ "w": 24,=
+
+ "x": 0,<=
+span class=3D"w">
+ "y": 0
+ },
+ "hiddenSeries": false,
+ "id": 2,
+ "legend": {
+ "avg": false,=
+span>
+ "current": false,
+ "max": false,=
+span>
+ "min": false,=
+span>
+ "show": true,=
+span>
+ "total":<=
+span class=3D"w"> false,=
+
+ "values":=
+ false
+ },
+ "lines": true,
+ "linewidth": 1,=
+span>
+ "nullPointMode":=
+span> "null",
+ "options":<=
+span class=3D"w"> {
+ "alertThreshold"=
+: true
+ },
+ "percentage": false,
+ "pluginVersion":=
+span> "7.3.5",
+ "pointradius": 2,=
+
+ "points": false,<=
+/span>
+ "renderer":=
+ "flot",
+ "seriesOverrides":=
+ [],
+ "spaceLength": 10=
+,
+ "stack": false,=
+span>
+ "steppedLine": false,
+ "targets":<=
+span class=3D"w"> [
+ {
+ "body":=
+ "",=
+span>
+ "cacheDurationSeconds": 0,
+ "fields": [
+ {
+ "jsonPath"=
+: "$.data[*].time",
+ "name": "time",
+ "type": "time"
+ },
+ {
+ "jsonPath"=
+: "$.data[*].inoctets"=
+span>,
+ "name": "inoctets",
+ "type": "number"
+ },
+ {
+ "jsonPath"=
+: "$.data[*].outoctets"<=
+/span>,
+ "name": "outoctets",
+ "type": "number"
+ }
+ ],
+ "hide":=
+ false=
+,
+ "method": "GET",
+ "params": [
+ [
+ "",=
+
+ ""
+ ]
+ ],
+ "queryParams":=
+ "",
+ "refId": "A",=
+
+ "urlPath": "?rrd_path=3D/opt/librenms/=
+rrd/$device/port-id$portid.rrd&epoch_start_time=3D${__from:date:seconds=
+}&epoch_end_time=3D${__to:date:seconds}"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom":=
+ null,=
+
+ "timeRegions": [],
+ "timeShift": null=
+,
+ "title": "Bandwidth In/Out",
+ "tooltip":<=
+span class=3D"w"> {
+ "shared":=
+ true,=
+
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "transformations":=
+ [
+ {
+ "id": "calculateField",
+ "options": {
+ "alias": "inMbps",
+ "binary": {
+ "left": "inoctets",
+ "operator"=
+: "*",
+ "reducer":=
+ "sum",
+ "right":=
+span> "8"
+ },
+ "mode": "binary",
+ "reduce": {
+ "include":=
+ [
+ "inoctets"
+ ],
+ "reducer":=
+ "lastNotNull"
+ },
+ "replaceFields": false
+ }
+ },
+ {
+ "id": "calculateField",
+ "options": {
+ "alias": "outMbps",
+ "binary": {
+ "left": "outoctets",
+ "operator"=
+: "*",
+ "reducer":=
+ "sum",
+ "right":=
+span> "8"
+ },
+ "mode": "binary",
+ "reduce": {
+ "reducer":=
+ "sum"
+ },
+ "replaceFields": false
+ }
+ },
+ {
+ "id": "filterFieldsByName",
+ "options": {
+ "include":=
+span> {
+ "names":=
+span> [
+ "time",<=
+/span>
+ "inMbps"=
+,
+ "outMbps"
+ ]
+ }
+ }
+ }
+ ],
+ "type": "graph",<=
+/span>
+ "xaxis": {
+ "buckets": null=
+,
+ "mode": "time",=
+
+ "name": null,=
+span>
+ "show": true,=
+span>
+ "values":=
+ []
+ },
+ "yaxes": [
+ {
+ "$$hashKey":=
+span> "object:1546",
+ "format": "bps",
+ "label": null=
+,
+ "logBase": 1,=
+
+ "max":<=
+span class=3D"w"> null,<=
+/span>
+ "min":<=
+span class=3D"w"> null,<=
+/span>
+ "show":=
+ true
+ },
+ {
+ "$$hashKey":=
+span> "object:1547",
+ "format": "short",
+ "label": null=
+,
+ "logBase": 1,=
+
+ "max":<=
+span class=3D"w"> null,<=
+/span>
+ "min":<=
+span class=3D"w"> null,<=
+/span>
+ "show":=
+ true
+ }
+ ],
+ "yaxis": {
+ "align":<=
+span class=3D"w"> false,=
+
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,=
+
+ "dashes": false,<=
+/span>
+ "datasource": "${DS_RRDREST_API}",
+ "fieldConfig": {
+ "defaults": {
+ "custom": {},
+ "unit":=
+ "bps"
+ },
+ "overrides": []
+ },
+ "fill": 1,=
+
+ "fillGradient": 0=
+,
+ "gridPos":<=
+span class=3D"w"> {
+ "h": 15,=
+
+ "w": 24,=
+
+ "x": 0,<=
+span class=3D"w">
+ "y": 15
+ },
+ "hiddenSeries": false,
+ "id": 3,
+ "legend": {
+ "avg": false,=
+span>
+ "current": false,
+ "max": false,=
+span>
+ "min": false,=
+span>
+ "show": true,=
+span>
+ "total":<=
+span class=3D"w"> false,=
+
+ "values":=
+ false
+ },
+ "lines": true,
+ "linewidth": 1,=
+span>
+ "nullPointMode":=
+span> "null",
+ "options":<=
+span class=3D"w"> {
+ "alertThreshold"=
+: true
+ },
+ "percentage": false,
+ "pluginVersion":=
+span> "7.3.5",
+ "pointradius": 2,=
+
+ "points": false,<=
+/span>
+ "renderer":=
+ "flot",
+ "seriesOverrides":=
+ [],
+ "spaceLength": 10=
+,
+ "stack": false,=
+span>
+ "steppedLine": false,
+ "targets":<=
+span class=3D"w"> [
+ {
+ "body":=
+ "",=
+span>
+ "cacheDurationSeconds": 0,
+ "fields": [
+ {
+ "jsonPath"=
+: "$.data[*].time",
+ "name": "time",
+ "type": "time"
+ },
+ {
+ "jsonPath"=
+: "$.data[*].inerrors"=
+span>,
+ "name": "inerrors",
+ "type": "number"
+ },
+ {
+ "jsonPath"=
+: "$.data[*].outerrors"<=
+/span>,
+ "name": "outerrors",
+ "type": "number"
+ }
+ ],
+ "hide":=
+ false=
+,
+ "method": "GET",
+ "params": [
+ [
+ "",=
+
+ ""
+ ]
+ ],
+ "queryParams":=
+ "",
+ "refId": "A",=
+
+ "urlPath": "?rrd_path=3D/opt/librenms/=
+rrd/$device/port-id$portid.rrd&epoch_start_time=3D${__from:date:seconds=
+}&epoch_end_time=3D${__to:date:seconds}"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom":=
+ null,=
+
+ "timeRegions": [],
+ "timeShift": null=
+,
+ "title": "Errors In/Out",
+ "tooltip":<=
+span class=3D"w"> {
+ "shared":=
+ true,=
+
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "transformations":=
+ [],
+ "type": "graph",<=
+/span>
+ "xaxis": {
+ "buckets": null=
+,
+ "mode": "time",=
+
+ "name": null,=
+span>
+ "show": true,=
+span>
+ "values":=
+ []
+ },
+ "yaxes": [
+ {
+ "$$hashKey":=
+span> "object:1546",
+ "format": "bps",
+ "label": null=
+,
+ "logBase": 1,=
+
+ "max":<=
+span class=3D"w"> null,<=
+/span>
+ "min":<=
+span class=3D"w"> null,<=
+/span>
+ "show":=
+ true
+ },
+ {
+ "$$hashKey":=
+span> "object:1547",
+ "format": "short",
+ "label": null=
+,
+ "logBase": 1,=
+
+ "max":<=
+span class=3D"w"> null,<=
+/span>
+ "min":<=
+span class=3D"w"> null,<=
+/span>
+ "show":=
+ true
+ }
+ ],
+ "yaxis": {
+ "align":<=
+span class=3D"w"> false,=
+
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "30s",
+ "schemaVersion": 26,<=
+/span>
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_LIBRENMS_API}",
+ "definition": "$.devices[*].hostname",
+ "error":<=
+span class=3D"w"> null,<=
+/span>
+ "hide": 0,
+ "includeAll": false,
+ "label":<=
+span class=3D"w"> null,<=
+/span>
+ "multi":<=
+span class=3D"w"> false,=
+
+ "name": "device",
+ "options": [],
+ "query":<=
+span class=3D"w"> {
+ "cacheDurationSeconds": 300,
+ "fields": [
+ {
+ "jsonPath"=
+: "$.devices[*].hostname=
+"
+ }
+ ],
+ "method": "GET",
+ "queryParams":=
+ "",
+ "urlPath": "/devices"
+ },
+ "refresh": 1,=
+span>
+ "regex":<=
+span class=3D"w"> "",
+ "skipUrlSync":=
+span> false,
+ "sort": 0,
+ "tagValuesQuery"=
+: "",
+ "tags": [],
+ "tagsQuery": ""=
+,
+ "type": "query"=
+,
+ "useTags": false
+ },
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_LIBRENMS_API}",
+ "definition": "$.ports[*].port_id",
+ "error":<=
+span class=3D"w"> null,<=
+/span>
+ "hide": 0,
+ "includeAll": false,
+ "label":<=
+span class=3D"w"> "Interface",
+ "multi":<=
+span class=3D"w"> false,=
+
+ "name": "portid",
+ "options": [],
+ "query":<=
+span class=3D"w"> {
+ "cacheDurationSeconds": 300,
+ "experimentalVariableTextField"<=
+span class=3D"p">: "ifNa=
+me",
+ "experimentalVariableValueField"=
+: "id"=
+,
+ "fields": [
+ {
+ "jsonPath"=
+: "$.ports[*].port_id"=
+span>,
+ "name": "id"
+ },
+ {
+ "jsonPath"=
+: "$.ports[*].ifName",
+ "name": ""
+ }
+ ],
+ "method": "GET",
+ "queryParams":=
+ "",
+ "urlPath": "/devices/$device/ports?col=
+umns=3DifName%2Cport_id"
+ },
+ "refresh": 1,=
+span>
+ "regex":<=
+span class=3D"w"> "",
+ "skipUrlSync":=
+span> false,
+ "sort": 0,
+ "tagValuesQuery"=
+: "",
+ "tags": [],
+ "tagsQuery": ""=
+,
+ "type": "query"=
+,
+ "useTags": false
+ }
+ ]
+ },
+ "time": {
+ "from": "now-12h",<=
+/span>
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "utc",
+ "title": "LibreNMS RRDReST",
+ "uid": "mFveL2mm1",=
+span>
+ "version": 28
+}
+
+ +
-
+ =20
+
-
+
+ Previous
+ Resolving my DNS issues - Disabling systemd-r= +esolved on Ubuntu 18.04 + +
+ =20
+ =20
+
+
+ CATALOG +
+- Overview
- Containerised Deployment
- LibreNMS
- RRDReST
- Grafana
- docker-compose up
- Adding data sources in Grafana<= +/a>
- Cre= +ating LibreNMS user
- Adding LibreNMS data source in Grafana= +
- Adding RRDReST data source in Grafana
- Creating a dashboard
- Conclusion
- Appendices
- References
- Versions used
- Full Code
- Docker Compose file
- Grafana Dashboard= + JSON