feat(webui,dashboard): add RAM Usage card between Disk Usage and Uptime, live-verified on Pager
This commit is contained in:
@@ -3025,6 +3025,23 @@ def disk_data():
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def mem_data(path='/proc/meminfo'):
|
||||||
|
try:
|
||||||
|
vals = {}
|
||||||
|
with open(path) as f:
|
||||||
|
for line in f:
|
||||||
|
k, v = line.split(':')
|
||||||
|
vals[k] = int(v.strip().split()[0])
|
||||||
|
total = vals.get('MemTotal', 0)
|
||||||
|
avail = vals.get('MemAvailable', vals.get('MemFree', 0))
|
||||||
|
if not total:
|
||||||
|
return {}
|
||||||
|
used = max(total - avail, 0)
|
||||||
|
return {'size': total * 1024, 'used': used * 1024, 'avail': avail * 1024}
|
||||||
|
except (OSError, ValueError):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def uptime_data():
|
def uptime_data():
|
||||||
rc, out, err = device_run(['cat', '/proc/uptime'])
|
rc, out, err = device_run(['cat', '/proc/uptime'])
|
||||||
try:
|
try:
|
||||||
@@ -3066,6 +3083,7 @@ def status_data():
|
|||||||
'wifi': [wifi_iface_info(n) for n in ifaces],
|
'wifi': [wifi_iface_info(n) for n in ifaces],
|
||||||
'clients': assoc_clients(ifaces),
|
'clients': assoc_clients(ifaces),
|
||||||
'disk': disk_data(),
|
'disk': disk_data(),
|
||||||
|
'mem': mem_data(),
|
||||||
'uptime': uptime_data(),
|
'uptime': uptime_data(),
|
||||||
'hostname': hostname_data(),
|
'hostname': hostname_data(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ views.dashboard = (root) => {
|
|||||||
root.appendChild(grid);
|
root.appendChild(grid);
|
||||||
const defs = [
|
const defs = [
|
||||||
['clients', 'Clients Connected'], ['handshakes', 'Handshakes Captured'],
|
['clients', 'Clients Connected'], ['handshakes', 'Handshakes Captured'],
|
||||||
['disk', 'Disk Usage'], ['uptime', 'Uptime']
|
['disk', 'Disk Usage'], ['mem', 'RAM Usage'], ['uptime', 'Uptime']
|
||||||
];
|
];
|
||||||
const cards = {};
|
const cards = {};
|
||||||
defs.forEach(([k, label]) => {
|
defs.forEach(([k, label]) => {
|
||||||
@@ -318,6 +318,8 @@ views.dashboard = (root) => {
|
|||||||
cards.uptime.textContent = s.uptime == null ? 'Unavailable' : fmtDur(s.uptime);
|
cards.uptime.textContent = s.uptime == null ? 'Unavailable' : fmtDur(s.uptime);
|
||||||
cards.disk.textContent = s.disk && s.disk.size != null
|
cards.disk.textContent = s.disk && s.disk.size != null
|
||||||
? fmtBytes(s.disk.used) + ' / ' + fmtBytes(s.disk.size) : 'Unavailable';
|
? fmtBytes(s.disk.used) + ' / ' + fmtBytes(s.disk.size) : 'Unavailable';
|
||||||
|
cards.mem.textContent = s.mem && s.mem.size != null
|
||||||
|
? fmtBytes(s.mem.used) + ' / ' + fmtBytes(s.mem.size) : 'Unavailable';
|
||||||
if (typeof MiniChart !== 'undefined') {
|
if (typeof MiniChart !== 'undefined') {
|
||||||
MiniChart.draw(canvas, [
|
MiniChart.draw(canvas, [
|
||||||
{ label: 'Clients', color: '#1976d2', points: history.clients }
|
{ label: 'Clients', color: '#1976d2', points: history.clients }
|
||||||
@@ -329,6 +331,7 @@ views.dashboard = (root) => {
|
|||||||
.catch(() => {
|
.catch(() => {
|
||||||
cards.clients.textContent = '0';
|
cards.clients.textContent = '0';
|
||||||
cards.disk.textContent = '—';
|
cards.disk.textContent = '—';
|
||||||
|
cards.mem.textContent = '—';
|
||||||
cards.uptime.textContent = '—';
|
cards.uptime.textContent = '—';
|
||||||
});
|
});
|
||||||
PagerAPI.get('/api/pineap/handshakes').then((r) => {
|
PagerAPI.get('/api/pineap/handshakes').then((r) => {
|
||||||
|
|||||||
+15
-1
@@ -43,6 +43,20 @@ class StatusTest(unittest.TestCase):
|
|||||||
d = server.disk_data()
|
d = server.disk_data()
|
||||||
self.assertEqual(d['avail'], 7364608 * 1024)
|
self.assertEqual(d['avail'], 7364608 * 1024)
|
||||||
|
|
||||||
|
def test_mem_parses_meminfo(self):
|
||||||
|
import tempfile
|
||||||
|
base = tempfile.mkdtemp()
|
||||||
|
with open(os.path.join(base, 'meminfo'), 'w') as f:
|
||||||
|
f.write('MemTotal: 262144 kB\nMemFree: 102400 kB\nMemAvailable: 122880 kB\nBuffers: 20480 kB\n')
|
||||||
|
d = server.mem_data(os.path.join(base, 'meminfo'))
|
||||||
|
self.assertEqual(d['size'], 262144 * 1024)
|
||||||
|
self.assertEqual(d['used'], (262144 - 122880) * 1024)
|
||||||
|
self.assertEqual(d['avail'], 122880 * 1024)
|
||||||
|
|
||||||
|
def test_mem_missing_returns_empty(self):
|
||||||
|
import tempfile
|
||||||
|
self.assertEqual(server.mem_data(os.path.join(tempfile.mkdtemp(), 'nope')), {})
|
||||||
|
|
||||||
def test_wifi_ifaces_extracts_names(self):
|
def test_wifi_ifaces_extracts_names(self):
|
||||||
server.device_run = lambda args, timeout=20: (0, 'wlan0 ESSID: "Pineapple"\nwlan1 ESSID: "Pineapple"\n', '') if args == ['iwinfo'] else (0, '', '')
|
server.device_run = lambda args, timeout=20: (0, 'wlan0 ESSID: "Pineapple"\nwlan1 ESSID: "Pineapple"\n', '') if args == ['iwinfo'] else (0, '', '')
|
||||||
self.assertEqual(server.wifi_ifaces(), ['wlan0', 'wlan1'])
|
self.assertEqual(server.wifi_ifaces(), ['wlan0', 'wlan1'])
|
||||||
@@ -89,7 +103,7 @@ class StatusTest(unittest.TestCase):
|
|||||||
args = ()
|
args = ()
|
||||||
status, payload = server.h_status(Ctx())
|
status, payload = server.h_status(Ctx())
|
||||||
self.assertEqual(status, 200)
|
self.assertEqual(status, 200)
|
||||||
for k in ('battery', 'firmware', 'daemon', 'wifi', 'clients', 'disk', 'uptime', 'hostname'):
|
for k in ('battery', 'firmware', 'daemon', 'wifi', 'clients', 'disk', 'mem', 'uptime', 'hostname'):
|
||||||
self.assertIn(k, payload)
|
self.assertIn(k, payload)
|
||||||
|
|
||||||
def test_status_discovers_wifi_interfaces_once(self):
|
def test_status_discovers_wifi_interfaces_once(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user