From f8b416d4345fc23f121f32cafdf2a25fcba083c8 Mon Sep 17 00:00:00 2001 From: Stephan de Wit Date: Thu, 23 May 2024 10:47:25 +0200 Subject: [PATCH] dashboard: swap usage gauge --- .../OPNsense/Core/Api/DashboardController.php | 5 ++ .../OPNsense/Core/Api/SystemController.php | 5 ++ src/opnsense/scripts/system/swapinfo.py | 48 +++++++++++++++ .../conf/actions.d/actions_system.conf | 6 ++ src/opnsense/www/js/widgets/Swap.js | 58 +++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100755 src/opnsense/scripts/system/swapinfo.py create mode 100644 src/opnsense/www/js/widgets/Swap.js diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/DashboardController.php b/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/DashboardController.php index 4122b668b..e4df2f9ee 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/DashboardController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/DashboardController.php @@ -115,6 +115,11 @@ class DashboardController extends ApiControllerBase 'used' => gettext('Used'), 'free' => gettext('Free'), ], + 'swap' => [ + 'title' => gettext('SWAP Usage'), + 'used' => gettext('Used'), + 'free' => gettext('Free'), + ] ]; } diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/SystemController.php b/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/SystemController.php index 3db6b86b6..71a09782e 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/SystemController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Core/Api/SystemController.php @@ -327,4 +327,9 @@ class SystemController extends ApiControllerBase { return json_decode((new Backend())->configdRun('system show mbuf'), true); } + + public function systemSwapAction() + { + return json_decode((new Backend())->configdRun('system show swapinfo'), true); + } } diff --git a/src/opnsense/scripts/system/swapinfo.py b/src/opnsense/scripts/system/swapinfo.py new file mode 100755 index 000000000..2017daea1 --- /dev/null +++ b/src/opnsense/scripts/system/swapinfo.py @@ -0,0 +1,48 @@ +#!/usr/local/bin/python3 + +""" + Copyright (c) 2024 Deciso B.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +""" + +import subprocess +import json + +swapinfo_output = subprocess.check_output("/usr/sbin/swapinfo -k", shell=True).decode('utf-8') + +swap_data = [] + +for line in swapinfo_output.splitlines(): + if '/dev/' in line: + parts = line.split() + swap_item = { + "device": parts[0], + "total": parts[1], + "used": parts[2] + } + swap_data.append(swap_item) + +result = {"swap": swap_data} + +print(json.dumps(result)) diff --git a/src/opnsense/service/conf/actions.d/actions_system.conf b/src/opnsense/service/conf/actions.d/actions_system.conf index 4977726f6..3feafe82d 100644 --- a/src/opnsense/service/conf/actions.d/actions_system.conf +++ b/src/opnsense/service/conf/actions.d/actions_system.conf @@ -132,3 +132,9 @@ command:/usr/bin/netstat -m --libxo json parameters: type:script_output message:Show mbuf stats + +[show.swapinfo] +command:/usr/local/opnsense/scripts/system/swapinfo.py +parameters: +type:script_output +message:Show swap info diff --git a/src/opnsense/www/js/widgets/Swap.js b/src/opnsense/www/js/widgets/Swap.js new file mode 100644 index 000000000..09d9462da --- /dev/null +++ b/src/opnsense/www/js/widgets/Swap.js @@ -0,0 +1,58 @@ +// endpoint:/api/core/system/system_swap + +/* + * Copyright (C) 2024 Deciso B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import BaseGaugeWidget from "./BaseGaugeWidget.js"; + +export default class Swap extends BaseGaugeWidget { + constructor() { + super(); + this.tickTimeout = 15000; + } + + async onMarkupRendered() { + super.createGaugeChart({ + labels: [this.translations.used, this.translations.free], + secondaryText: (data) => { + return `(${data[0]} / ${data[0] + data[1]}) MB`; + } + }); + } + + async onWidgetTick() { + ajaxGet('/api/core/system/system_swap', {}, (data, status) => { + let total = 0; + let used = 0; + for (const swapDevice of data['swap']) { + total += parseInt(swapDevice.total); + used += parseInt(swapDevice.used); + } + + super.updateChart([(used / 1024), (total - used) / 1024]); + }); + } +}