mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
diagnostics: Show low system memory notifications
Containers specific case: if total memory taken from cgroups is lower than system memory taken from psutil, calculate memory usage based on information from cgroups. The formula idea is taken from https://github.com/moby/moby/issues/40727#issuecomment-604155288 Closes #1780 Tests performed: - In a non-container environment, filled the memory 90% ``` stress-ng --vm-bytes $(awk '/MemAvailable/{printf "%d\n", $2 * 0.9;}' \ < /proc/meminfo)k --vm-keep -m 1 ``` and ensured that correct notification is shown. - In a container, if no memory limitations are set, notifications are based on host memory usage - In a container, if memory limits are set ``` systemctl set-property systemd-nspawn@fbx-testing.service MemoryMax=200M ``` ensured that the notification is shown and is calculated based on cgroups. Signed-off-by: Veiko Aasa <veiko17@disroot.org> [sunil: Fix i18n for notification message] [sunil: Drop unnecessary type conversion] Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
0960a13d49
commit
d93f2f634d
@ -6,13 +6,15 @@ FreedomBox app for system diagnostics.
|
|||||||
import collections
|
import collections
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
|
import pathlib
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
import psutil
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ugettext_noop
|
from django.utils.translation import ugettext_noop
|
||||||
|
|
||||||
from plinth import app as app_module
|
from plinth import app as app_module
|
||||||
from plinth import daemon, menu
|
from plinth import cfg, daemon, glib, menu
|
||||||
from plinth.modules.apache.components import diagnose_url_on_all
|
from plinth.modules.apache.components import diagnose_url_on_all
|
||||||
|
|
||||||
from .manifest import backup # noqa, pylint: disable=unused-import
|
from .manifest import backup # noqa, pylint: disable=unused-import
|
||||||
@ -55,6 +57,10 @@ class DiagnosticsApp(app_module.App):
|
|||||||
'diagnostics:index', parent_url_name='system')
|
'diagnostics:index', parent_url_name='system')
|
||||||
self.add(menu_item)
|
self.add(menu_item)
|
||||||
|
|
||||||
|
# Check periodically for low RAM space
|
||||||
|
interval = 180 if cfg.develop else 3600
|
||||||
|
glib.schedule(interval, _warn_about_low_ram_space)
|
||||||
|
|
||||||
def diagnose(self):
|
def diagnose(self):
|
||||||
"""Run diagnostics and return the results."""
|
"""Run diagnostics and return the results."""
|
||||||
results = super().diagnose()
|
results = super().diagnose()
|
||||||
@ -126,3 +132,101 @@ def run_on_all_enabled_modules():
|
|||||||
current_results['results'][app_id] = app.diagnose()
|
current_results['results'][app_id] = app.diagnose()
|
||||||
current_results['progress_percentage'] = \
|
current_results['progress_percentage'] = \
|
||||||
int((current_index + 1) * 100 / len(apps))
|
int((current_index + 1) * 100 / len(apps))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_memory_info_from_cgroups():
|
||||||
|
"""Return information about RAM usage from cgroups."""
|
||||||
|
cgroups_memory_path = pathlib.Path('/sys/fs/cgroup/memory')
|
||||||
|
memory_limit_file = cgroups_memory_path / 'memory.limit_in_bytes'
|
||||||
|
memory_usage_file = cgroups_memory_path / 'memory.usage_in_bytes'
|
||||||
|
memory_stat_file = cgroups_memory_path / 'memory.stat'
|
||||||
|
|
||||||
|
try:
|
||||||
|
memory_total = int(memory_limit_file.read_text())
|
||||||
|
memory_usage = int(memory_usage_file.read_text())
|
||||||
|
memory_stat_lines = memory_stat_file.read_text().split('\n')
|
||||||
|
except OSError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
memory_inactive = int([
|
||||||
|
line.rsplit(maxsplit=1)[1] for line in memory_stat_lines
|
||||||
|
if line.startswith('total_inactive_file')
|
||||||
|
][0])
|
||||||
|
memory_used = memory_usage - memory_inactive
|
||||||
|
|
||||||
|
return {
|
||||||
|
'total_bytes': memory_total,
|
||||||
|
'percent_used': memory_used * 100 / memory_total,
|
||||||
|
'free_bytes': memory_total - memory_used
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_memory_info():
|
||||||
|
"""Return RAM usage information."""
|
||||||
|
memory_info = psutil.virtual_memory()
|
||||||
|
|
||||||
|
cgroups_memory_info = _get_memory_info_from_cgroups()
|
||||||
|
if cgroups_memory_info and cgroups_memory_info[
|
||||||
|
'total_bytes'] < memory_info.total:
|
||||||
|
return cgroups_memory_info
|
||||||
|
|
||||||
|
return {
|
||||||
|
'total_bytes': memory_info.total,
|
||||||
|
'percent_used': memory_info.percent,
|
||||||
|
'free_bytes': memory_info.available
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _warn_about_low_ram_space(request):
|
||||||
|
"""Warn about insufficient RAM space."""
|
||||||
|
from plinth.notification import Notification
|
||||||
|
|
||||||
|
memory_info = _get_memory_info()
|
||||||
|
if memory_info['free_bytes'] < 1024**3:
|
||||||
|
# Translators: This is the unit of computer storage Mebibyte similar to
|
||||||
|
# Megabyte.
|
||||||
|
memory_available_unit = ugettext_noop('MiB')
|
||||||
|
memory_available = memory_info['free_bytes'] / 1024**2
|
||||||
|
else:
|
||||||
|
# Translators: This is the unit of computer storage Gibibyte similar to
|
||||||
|
# Gigabyte.
|
||||||
|
memory_available_unit = ugettext_noop('GiB')
|
||||||
|
memory_available = memory_info['free_bytes'] / 1024**3
|
||||||
|
|
||||||
|
show = False
|
||||||
|
if memory_info['percent_used'] > 90:
|
||||||
|
severity = 'error'
|
||||||
|
advice_message = ugettext_noop(
|
||||||
|
'You should disable some apps to reduce memory usage.')
|
||||||
|
show = True
|
||||||
|
elif memory_info['percent_used'] > 75:
|
||||||
|
severity = 'warning'
|
||||||
|
advice_message = ugettext_noop(
|
||||||
|
'You should not install any new apps on this system.')
|
||||||
|
show = True
|
||||||
|
|
||||||
|
if not show:
|
||||||
|
try:
|
||||||
|
Notification.get('diagnostics-low-ram-space').delete()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
|
||||||
|
message = ugettext_noop(
|
||||||
|
# xgettext:no-python-format
|
||||||
|
'System is low on memory: {percent_used}% used, {memory_available} '
|
||||||
|
'{memory_available_unit} free. {advice_message}')
|
||||||
|
title = ugettext_noop('Low Memory')
|
||||||
|
data = {
|
||||||
|
'app_icon': 'fa-heartbeat',
|
||||||
|
'app_name': ugettext_noop('Diagnostics'),
|
||||||
|
'percent_used': f'{memory_info["percent_used"]:.1f}',
|
||||||
|
'memory_available': f'{memory_available:.1f}',
|
||||||
|
'memory_available_unit': 'translate:' + memory_available_unit,
|
||||||
|
'advice_message': 'translate:' + advice_message
|
||||||
|
}
|
||||||
|
actions = [{'type': 'dismiss'}]
|
||||||
|
Notification.update_or_create(id='diagnostics-low-ram-space',
|
||||||
|
app_id='diagnostics', severity=severity,
|
||||||
|
title=title, message=message,
|
||||||
|
actions=actions, data=data, group='admin')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user