Langzeitmonitoring von Freifunk-Nodes mit Google Tabellen und Google Apps Script
Langzeitmonitoring von Freifunk-Nodes mit Google Tabellen und Google Apps Script
Mithilfe von Google Tabellen und einem Google Apps Script (GAS) können auf einfache Art und Weise Langzeitstatistiken von Freifunk-Nodes erstellt werden. Alles was dazu benötigt wird, ist ein Google-Konto und die URL zu der nodes.json des jeweiligen ffmap-backends. Die periodische Datenabfrage und Datenhaltung übernimmt Google für uns. Die in Google Tabellen gesammelten Informationen können dann zum Beispiel als CSV exportiert (automatisiert heruntergeladen) und ausgewertet werden oder direkt in Google Tabellen durch Diagramme visualisiert werden.
Die folgende bebilderte Anleitung zeigt Schritt für Schritt wie es funktioniert.
- Schritt 1
Nach dem Einloggen in Google Drive wird als erstes eine neue Tabelle erstellt.
- Schritt 2
Innerhalb der neuen Tabelle wird der Skripteditor geöffnet.
- Schritt 3
Der Inhalt der bereits vorhanden Datei Code.gs wird durch dieses Skript ersetzt und anschließend gespeichert.
/* A Google Apps Script to periodically fetch nodes.json and push data into Google Sheets for selected nodes. Copyright (c) 2016 Florian Schäfer <florian.schaefer+freifunk@gmail.com> */ var NODES_JSON_URL = "http://gw2.freifunk-lueneburg.de/meshviewer/data/nodes.json"; // create a log for these nodes var NODES = [ "f81a675304a3", "687251628f83", "18a6f76be754" ]; // the date the script was triggered var date = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'"); // definition to map from JSON to a sheet column var DataMappings = [ { "caption": "Date", "fn": function (node) { return date; } }, { "caption": "ID", "fn": function (node) { return node.nodeinfo.node_id; } }, { "caption": "Hostname", "fn": function (node) { return node.nodeinfo.hostname; } }, { "caption": "Hardware", "fn": function (node) { return node.nodeinfo.hardware.model; } }, { "caption": "Online", "fn": function (node) { return node.flags.online; } }, { "caption": "Clients", "fn": function (node) { return node.statistics.clients; } }, { "caption": "Uptime", "fn": function (node) { return node.statistics.uptime; } }, { "caption": "Memory Usage", "fn": function (node) { return node.statistics.memory_usage; } }, { "caption": "Load Avg", "fn": function (node) { return node.statistics.loadavg; } }, { "caption": "First Seen", "fn": function (node) { return node.firstseen; } }, { "caption": "Last Seen", "fn": function (node) { return node.lastseen; } }, { "caption": "RX", "fn": function (node) { return node.statistics.traffic.rx.bytes; } }, { "caption": "TX", "fn": function (node) { return node.statistics.traffic.tx.bytes; } } ]; function FFNodeMonitor (endpoint, nodes) { this.endpoint = endpoint; this.nodes = nodes; this.activeSpreadSheet = SpreadsheetApp.getActive(); this.activeSheet = SpreadsheetApp.getActiveSheet(); // check if the clock based trigger is already installed this.triggerId = PropertiesService.getScriptProperties().getProperty("triggerId"); // if not, do so if (!this.triggerId) { this.triggerId = ScriptApp.newTrigger("main") .timeBased() .everyMinutes(15) .create() .getUniqueId(); PropertiesService.getScriptProperties().setProperty("triggerId", this.triggerId); } } FFNodeMonitor.prototype.fetchData = function () { // fetch the data from endpoint and parse it to JSON var response = UrlFetchApp.fetch(this.endpoint); this.data = JSON.parse(response); return this; }; FFNodeMonitor.prototype.fillCells = function () { this.nodes.forEach(function (nodeId, i) { var scheetName = nodeId; // create one sheet per node with the node id as name this.dataSheet = this.activeSpreadSheet.getSheetByName(scheetName); if (!this.dataSheet) { this.dataSheet = this.activeSpreadSheet.insertSheet(scheetName); } // setup column head lines DataMappings .forEach(function (mapping, i) { this.dataSheet.getRange(String.fromCharCode(65 + i) + 1).setValue(mapping.caption); }.bind(this)); // get last free row and fill it with data from mapping var offset = this.dataSheet.getLastRow() + 1; DataMappings .forEach(function (mapping, i) { this.dataSheet.getRange(String.fromCharCode(65 + i) + offset).setValue(mapping.fn(this.data.nodes[nodeId])); this.dataSheet.autoResizeColumn(i + 1); }.bind(this)); }.bind(this)); }; function main() { new FFNodeMonitor(NODES_JSON_URL, NODES) .fetchData() .fillCells(); }
- Schritt 4
In dem Menü Funktion auswählen wird die Funktion main des Google Apps Skripts ausgewählt.
- Schritt 5
Mit der Variable NODES_JSON_URL wird die URL zur nodes.json des ffmap-backends angegeben. Im Array NODES werden die zu protokollierenden Node-IDs eingetragen.
- Schritt 6
Nachdem die Node-IDs eingetragen wurden, wird nun das Skript zum ersten Mal ausgeführt.
- Schritt 7
Bevor die Ausführung des Skripts beginnt, müssen dem Skript einmalig Berechtigungen erteilt werden.
- Schritt 8
Nach der Bestätigung wird das Skript nun gestartet.
- Schritt 9
Das Skript legt in der Google Tabelle für jede Node-ID ein neues Tabellenblatt an und schreibt die abgerufenen Daten für jeden Node in die erste leere Zeile der Tabelle.
- Schritt 10
Das Skript erstellt programmatisch einen Trigger. Dieser ruft das Skript alle 15 Minuten auf, auch wenn die Tabelle im Browser geschlossen ist. Dieser Trigger kann durch einen Klick auf die Schaltfläche Trigger des aktuellen Projekts kontrolliert und angepasst werden.
- Anmerkungen
Das Skript ist ein Prototyp und wurde nur mit der nodes.json der Freifunk Lueneburg Community getestet. Es sollte aber auch mit den Servern der anderen Communities funktionieren. Mit ein paar kleinen Änderungen könnten auch Nachrichten (z. B. E-Mails) bei bestimmten Ereignissen (z. B. Clientanzahl > X) versendet werden.