storage: simplified use of df to get disk information

- Use df --block-size=1 instead --human-readable to fix locale issue #1043.
- Code cleanup: get available space directly, better variable/module names.

Signed-off-by: Johannes Keyser <johanneskeyser@posteo.de>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Johannes Keyser 2017-10-08 16:58:14 +02:00 committed by James Valleroy
parent 1247f2ef93
commit 10df3004ac
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
3 changed files with 59 additions and 74 deletions

View File

@ -64,8 +64,8 @@ def get_disks():
def _get_disks_from_df():
"""Return the list of disks and free space available using 'df'."""
command = ['df', '--exclude-type=tmpfs', '--exclude-type=devtmpfs',
'--output=source,target,fstype,size,used,pcent',
'--human-readable']
'--block-size=1',
'--output=source,target,fstype,size,used,avail,pcent']
try:
process = subprocess.run(command, stdout=subprocess.PIPE, check=True)
except subprocess.CalledProcessError as exception:
@ -78,9 +78,15 @@ def _get_disks_from_df():
for line in output.splitlines()[1:]:
parts = line.split()
keys = ('device', 'mount_point', 'file_system_type', 'size', 'used',
'percentage_used')
'free', 'percent_used')
disk = dict(zip(keys, parts))
disk['percentage_used'] = int(disk['percentage_used'].rstrip('%'))
disk['percent_used'] = int(disk['percent_used'].rstrip('%'))
disk['size'] = int(disk['size'])
disk['used'] = int(disk['used'])
disk['free'] = int(disk['free'])
disk['size_str'] = format_bytes(disk['size'])
disk['used_str'] = format_bytes(disk['used'])
disk['free_str'] = format_bytes(disk['free'])
disks.append(disk)
return disks
@ -130,3 +136,27 @@ def is_expandable(device):
def expand_partition(device):
"""Expand a partition."""
actions.superuser_run('storage', ['expand-partition', device])
def format_bytes(size):
"""Return human readable disk size from bytes."""
if not size:
return size
if size < 1024:
return _('{disk_size:.1f} bytes').format(disk_size=size)
if size < 1024 ** 2:
size /= 1024
return _('{disk_size:.1f} KiB').format(disk_size=size)
if size < 1024 ** 3:
size /= 1024 ** 2
return _('{disk_size:.1f} MiB').format(disk_size=size)
if size < 1024 ** 4:
size /= 1024 ** 3
return _('{disk_size:.1f} GiB').format(disk_size=size)
size /= 1024 ** 4
return _('{disk_size:.1f} TiB').format(disk_size=size)

View File

@ -52,20 +52,20 @@
<td>{{ disk.file_system_type }}</td>
<td>
<div class="progress">
{% if disk.percentage_used < 75 %}
{% if disk.percent_used < 75 %}
<div class="progress-bar progress-bar-striped progress-bar-success"
{% elif disk.percentage_used < 90 %}
{% elif disk.percent_used < 90 %}
<div class="progress-bar progress-bar-striped progress-bar-warning"
{% else %}
<div class="progress-bar progress-bar-striped progress-bar-danger"
{% endif %}
role="progressbar" aria-valuenow="disk.percentage_used"
role="progressbar" aria-valuenow="disk.percent_used"
aria-valuemin="0" aria-valuemax="100"
style="width: {{ disk.percentage_used }}%;">
{{ disk.percentage_used }}%
style="width: {{ disk.percent_used }}%;">
{{ disk.percent_used }}%
</div>
</div>
<div>{{ disk.used }} / {{ disk.size }}</div>
<div>{{ disk.used_str }} / {{ disk.size_str }}</div>
</td>
</tr>
{% endfor %}

View File

@ -25,7 +25,7 @@ from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.translation import ugettext as _
from plinth.modules import storage as storage_module
from plinth.modules import storage
from plinth.utils import format_lazy, is_user_admin
@ -34,10 +34,10 @@ logger = logging.getLogger(__name__)
def index(request):
"""Show connection list."""
disks = storage_module.get_disks()
root_device = storage_module.get_root_device(disks)
expandable_root_size = storage_module.is_expandable(root_device)
expandable_root_size = _format_bytes(expandable_root_size)
disks = storage.get_disks()
root_device = storage.get_root_device(disks)
expandable_root_size = storage.is_expandable(root_device)
expandable_root_size = storage.format_bytes(expandable_root_size)
warn_about_low_disk_space(request)
@ -49,15 +49,15 @@ def index(request):
def expand(request):
"""Warn and expand the root partition."""
disks = storage_module.get_disks()
root_device = storage_module.get_root_device(disks)
disks = storage.get_disks()
root_device = storage.get_root_device(disks)
if request.method == 'POST':
expand_partition(request, root_device)
return redirect(reverse('storage:index'))
expandable_root_size = storage_module.is_expandable(root_device)
expandable_root_size = _format_bytes(expandable_root_size)
expandable_root_size = storage.is_expandable(root_device)
expandable_root_size = storage.format_bytes(expandable_root_size)
return TemplateResponse(request, 'storage_expand.html',
{'title': _('Expand Root Partition'),
'expandable_root_size': expandable_root_size})
@ -66,7 +66,7 @@ def expand(request):
def expand_partition(request, device):
"""Expand the partition."""
try:
storage_module.expand_partition(device)
storage.expand_partition(device)
except Exception as exception:
messages.error(request, _('Error expanding partition: {exception}')
.format(exception=exception))
@ -79,71 +79,26 @@ def warn_about_low_disk_space(request):
if not is_user_admin(request, cached=True):
return
disks = storage_module.get_disks()
disks = storage.get_disks()
list_root = [disk for disk in disks if disk['mountpoint'] == '/']
if not list_root:
logger.error('Error getting information about root partition.')
return
perc_used = list_root[0]['percentage_used']
size_bytes = _interpret_size_string(list_root[0]['size'])
free_bytes = size_bytes * (100 - perc_used) / 100
percent_used = list_root[0]['percent_used']
size_bytes = list_root[0]['size']
free_bytes = list_root[0]['free']
free_gib = free_bytes / (1024 ** 3)
message = format_lazy(
# Translators: xgettext:no-python-format
_('Warning: Low space on system partition ({percent_used}% used, '
'{free_space} free).'),
percent_used=perc_used, free_space=_format_bytes(free_bytes))
percent_used=percent_used,
free_space=storage.format_bytes(free_bytes))
free_gib = free_bytes / (1024 ** 3)
if perc_used > 90 or free_gib < 1:
if percent_used > 90 or free_gib < 1:
messages.error(request, message)
elif perc_used > 75 or free_gib < 2:
elif percent_used > 75 or free_gib < 2:
messages.warning(request, message)
def _interpret_size_string(size_str):
"""Convert size string to number of bytes."""
# TODO: Drop --human-readable from df command (github issue #1043)
size_str = size_str.replace(',', '.') # some locales use commas
size_str = size_str.replace('٫', '.') # arabic decimal separator
if size_str[-1] in '0123456789':
return float(size_str[:-1])
if size_str[-1] == 'K':
return float(size_str[:-1]) * 1024
if size_str[-1] == 'M':
return float(size_str[:-1]) * 1024 ** 2
if size_str[-1] == 'G':
return float(size_str[:-1]) * 1024 ** 3
if size_str[-1] == 'T':
return float(size_str[:-1]) * 1024 ** 4
return -1
def _format_bytes(size):
"""Return human readable disk size from bytes."""
if not size:
return size
if size < 1024:
return _('{disk_size:.1f} bytes').format(disk_size=size)
if size < 1024 ** 2:
size /= 1024
return _('{disk_size:.1f} KiB').format(disk_size=size)
if size < 1024 ** 3:
size /= 1024 ** 2
return _('{disk_size:.1f} MiB').format(disk_size=size)
if size < 1024 ** 4:
size /= 1024 ** 3
return _('{disk_size:.1f} GiB').format(disk_size=size)
size /= 1024 ** 4
return _('{disk_size:.1f} TiB').format(disk_size=size)