Skip to content

Commit

Permalink
dashboard: memory widget
Browse files Browse the repository at this point in the history
Also some minor performance improvements as suggested by https://www.chartjs.org/docs/latest/general/performance.html.

in contrast to the old dashboard memory calculation, this widget does not consider ARC to be part of used memory anymore.
  • Loading branch information
swhite2 committed Apr 19, 2024
1 parent 6d770f2 commit a2e14b2
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ private function getTranslations()
'trafficin' => gettext('Traffic In'),
'trafficout' => gettext('Traffic Out'),
],
'memory' => [
'title' => gettext('Memory'),
'used' => gettext('Used'),
'free' => gettext('Free'),
'arc' => gettext('ARC'),
]
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,4 +260,39 @@ public function systemTimeAction()

return json_encode($response);
}

public function systemResourcesAction()
{
$result = [];

$mem = json_decode((new Backend())->configdpRun('system sysctl values', implode(',', [
'hw.physmem',
'vm.stats.vm.v_page_count',
'vm.stats.vm.v_inactive_count',
'vm.stats.vm.v_cache_count',
'vm.stats.vm.v_free_count',
'kstat.zfs.misc.arcstats.size'
])), true);

if (!empty($mem['vm.stats.vm.v_page_count'])) {
$pc = $mem['vm.stats.vm.v_page_count'];
$ic = $mem['vm.stats.vm.v_inactive_count'];
$cc = $mem['vm.stats.vm.v_cache_count'];
$fc = $mem['vm.stats.vm.v_free_count'];
$result['memory']['total'] = $mem['hw.physmem'];
$result['memory']['total_frmt'] = sprintf('%d', $mem['hw.physmem'] / 1024 / 1024);
$result['memory']['used'] = round(((($pc - ($ic + $cc + $fc))) / $pc) * $mem['hw.physmem'], 0);
$result['memory']['used_frmt'] = sprintf('%d', $result['memory']['used'] / 1024 / 1024);
if (!empty($mem['kstat.zfs.misc.arcstats.size'])) {
$arc_size = $mem['kstat.zfs.misc.arcstats.size'];
$result['memory']['arc'] = $arc_size;
$result['memory']['arc_frmt'] = sprintf('%d', $arc_size / 1024 / 1024);
$result['memory']['arc_txt'] = sprintf(gettext('ARC size %d MB'), $arc_size / 1024 / 1024);
}
} else {
$result['memory']['used'] = gettext('N/A');
}

return json_encode($result);
}
}
2 changes: 1 addition & 1 deletion src/opnsense/mvc/app/views/OPNsense/Core/dashboard.volt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
$( document ).ready(function() {
let widgetManager = new WidgetManager({
float: false,
column: 4,
column: 5,
margin: 10,
alwaysShowResizeHandle: false,
sizeToContent: true,
Expand Down
20 changes: 0 additions & 20 deletions src/opnsense/www/css/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -172,26 +172,6 @@ td {
z-index: 20;
}

/* Gauge */
#gauge-container {
width: 80%;
margin: 5px auto;
background-color: #ccc;
height: 10px;
position: relative;
border-radius: 5px;
overflow: hidden;
}

#gauge-fill {
height: 100%;
width: 0;
background-color: green;
border-radius: 5px;
transition: width 0.5s, background-color 0.5s;
}


/* Custom flex table */
div {
box-sizing: border-box;
Expand Down
5 changes: 5 additions & 0 deletions src/opnsense/www/js/widgets/BaseWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,9 @@ export default class BaseWidget {
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

_setAlpha(color, opacity) {
const op = Math.round(Math.min(Math.max(opacity || 1, 0), 1) * 255);
return color + op.toString(16).toUpperCase();
}
}
6 changes: 1 addition & 5 deletions src/opnsense/www/js/widgets/InterfaceStatistics.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,6 @@ export default class InterfaceStatistics extends BaseWidget {
`);
}

_setAlpha(color, opacity) {
const op = Math.round(Math.min(Math.max(opacity || 1, 0), 1) * 255);
return color + op.toString(16).toUpperCase();
}

_getIndexedData(data) {
let indexedData = Array(this.labels.length).fill(null);
let indexedColors = Array(this.labels.length).fill(null);
Expand Down Expand Up @@ -133,6 +128,7 @@ export default class InterfaceStatistics extends BaseWidget {
layout: {
padding: 10
},
normalized: true,
parsing: false,
plugins: {
legend: {
Expand Down
131 changes: 131 additions & 0 deletions src/opnsense/www/js/widgets/Memory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* 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 BaseWidget from "./BaseWidget.js";

export default class Memory extends BaseWidget {
constructor() {
super();
this.tickTimeout = 15000;

this.chart = null;
this.curMemUsed = null;
this.curMemTotal = null;
}

getMarkup() {
return $(`
<div class="memory-chart-container">
<div class="canvas-container">
<canvas id="memory-chart"></canvas>
</div>
</div>
`);
}

async onMarkupRendered() {
let context = document.getElementById("memory-chart").getContext("2d");
let colorMap = ['#D94F00', '#A8C49B', '#E5E5E5'];

let config = {
type: 'doughnut',
data: {
labels: [this.translations.used, this.translations.arc, this.translations.free],
datasets: [
{
data: [],
backgroundColor: colorMap,
hoverBackgroundColor: colorMap.map((color) => this._setAlpha(color, 0.5)),
hoveroffset: 50,
fill: true
},
]
},
options: {
responsive: true,
maintainAspectRatio: true,
aspectRatio: 2,
layout: {
padding: 10
},
cutout: '64%',
rotation: 270,
circumference: 180,
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: (tooltipItem) => {
return `${tooltipItem.label}: ${tooltipItem.parsed} MB`;
}
}
}
}
},
plugins: [{
id: 'custom_positioned_text',
beforeDatasetsDraw: (chart, args, options) => {
// custom plugin: draw text at 2/3 y position of chart
if (chart.config.data.datasets[0].data.length !== 0) {
let width = chart.width;
let height = chart.height;
let ctx = chart.ctx;
ctx.restore();
let fontSize = (height / 114).toFixed(2);
ctx.font = fontSize + "em SourceSansProSemibold";
ctx.textBaseline = "middle";
let text = (this.curMemUsed / this.curMemTotal * 100).toFixed(2) + "%";
let textX = Math.round((width - ctx.measureText(text).width) / 2);
let textY = (height / 3) * 2;
ctx.fillText(text, textX, textY);
ctx.save();
}
}
}]
}

this.chart = new Chart(context, config);
}

async onWidgetTick() {
ajaxGet('/api/core/system/systemResources', {}, (data, status) => {
if (data.memory.total !== undefined) {
let used = parseInt(data.memory.used_frmt);
let arc = data.memory.hasOwnProperty('arc') ? parseInt(data.memory.arc_frmt) : 0;
let total = parseInt(data.memory.total_frmt);
let result = [(used - arc), arc, total - used];
this.chart.config.data.datasets[0].data = result

this.curMemUsed = used - arc;
this.curMemTotal = total;
this.chart.update();
}
});
}
}
5 changes: 3 additions & 2 deletions src/opnsense/www/js/widgets/Traffic.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export default class Traffic extends BaseWidget {
pointDot: false,
scaleShowGridLines: true,
responsive: true,
normalized: true,
elements: {
line: {
fill: true,
Expand All @@ -98,7 +99,7 @@ export default class Traffic extends BaseWidget {
type: 'realtime',
realtime: {
duration: 20000,
delay: 1000,
delay: 2000,
},
},
y: {
Expand Down Expand Up @@ -213,7 +214,7 @@ export default class Traffic extends BaseWidget {
}

async onMarkupRendered() {
this.eventSource = new EventSource('/api/diagnostics/traffic/stream/1');
this.eventSource = new EventSource('/api/diagnostics/traffic/stream/2');
this.eventSource.onmessage = this._onMessage.bind(this);
}

Expand Down

0 comments on commit a2e14b2

Please sign in to comment.