mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-27 10:44:33 +00:00
udiskie: Merge into storage module
udiskie is now an essential module that will be installed along with storage. Signed-off-by: Joseph Nuthalapati <njoseph@thoughtworks.com> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
f172925d9d
commit
a307476634
@ -1 +0,0 @@
|
|||||||
plinth.modules.udiskie
|
|
||||||
@ -27,7 +27,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth.menu import main_menu
|
from plinth.menu import main_menu
|
||||||
from plinth.modules import udiskie
|
from plinth.modules import storage
|
||||||
|
|
||||||
from .backups import backup_apps, restore_apps
|
from .backups import backup_apps, restore_apps
|
||||||
|
|
||||||
@ -118,8 +118,8 @@ def export_archive(name, location):
|
|||||||
def get_export_locations():
|
def get_export_locations():
|
||||||
"""Return a list of storage locations for exported backup archives."""
|
"""Return a list of storage locations for exported backup archives."""
|
||||||
locations = [('/var/lib/freedombox/', _('Root Filesystem'))]
|
locations = [('/var/lib/freedombox/', _('Root Filesystem'))]
|
||||||
if udiskie.is_running():
|
if storage.is_running():
|
||||||
devices = udiskie.udisks2.list_devices()
|
devices = storage.udisks2.list_devices()
|
||||||
for device in devices:
|
for device in devices:
|
||||||
if 'mount_points' in device and len(device['mount_points']) > 0:
|
if 'mount_points' in device and len(device['mount_points']) > 0:
|
||||||
name = device['label'] or device['device']
|
name = device['label'] or device['device']
|
||||||
|
|||||||
@ -17,21 +17,32 @@
|
|||||||
"""
|
"""
|
||||||
FreedomBox app to manage storage.
|
FreedomBox app to manage storage.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import service as service_module
|
||||||
|
from plinth import action_utils, actions, cfg
|
||||||
from plinth.menu import main_menu
|
from plinth.menu import main_menu
|
||||||
|
from plinth.utils import format_lazy
|
||||||
|
|
||||||
version = 2
|
version = 3
|
||||||
|
|
||||||
name = _('Storage')
|
name = _('Storage')
|
||||||
|
|
||||||
description = []
|
managed_services = ['freedombox-udiskie']
|
||||||
|
|
||||||
|
managed_packages = ['udiskie', 'gir1.2-udisks-2.0']
|
||||||
|
|
||||||
|
description = [
|
||||||
|
format_lazy(
|
||||||
|
_('This module allows you to manage storage media attached to your '
|
||||||
|
'{box_name}. You can view the storage media currently in use, mount '
|
||||||
|
'and unmount removable media, expand the root partition etc.'),
|
||||||
|
box_name=_(cfg.box_name))
|
||||||
|
]
|
||||||
|
|
||||||
service = None
|
service = None
|
||||||
|
|
||||||
@ -81,8 +92,8 @@ def _get_disks_from_df():
|
|||||||
disks = []
|
disks = []
|
||||||
for line in output.splitlines()[1:]:
|
for line in output.splitlines()[1:]:
|
||||||
parts = line.split(maxsplit=6)
|
parts = line.split(maxsplit=6)
|
||||||
keys = ('device', 'file_system_type', 'size', 'used',
|
keys = ('device', 'file_system_type', 'size', 'used', 'free',
|
||||||
'free', 'percent_used', 'mount_point')
|
'percent_used', 'mount_point')
|
||||||
disk = dict(zip(keys, parts))
|
disk = dict(zip(keys, parts))
|
||||||
disk['percent_used'] = int(disk['percent_used'].rstrip('%'))
|
disk['percent_used'] = int(disk['percent_used'].rstrip('%'))
|
||||||
disk['size'] = int(disk['size'])
|
disk['size'] = int(disk['size'])
|
||||||
@ -168,8 +179,37 @@ def format_bytes(size):
|
|||||||
return _('{disk_size:.1f} TiB').format(disk_size=size)
|
return _('{disk_size:.1f} TiB').format(disk_size=size)
|
||||||
|
|
||||||
|
|
||||||
|
def is_running():
|
||||||
|
"""Return whether the service is running."""
|
||||||
|
return action_utils.service_is_running('freedombox-udiskie')
|
||||||
|
|
||||||
|
|
||||||
|
def is_enabled():
|
||||||
|
"""Return whether the module is enabled."""
|
||||||
|
return action_utils.service_is_enabled('freedombox-udiskie')
|
||||||
|
|
||||||
|
|
||||||
|
def enable():
|
||||||
|
"""Enable the module."""
|
||||||
|
actions.superuser_run('udiskie', ['enable'])
|
||||||
|
|
||||||
|
|
||||||
|
def disable():
|
||||||
|
"""Disable the module."""
|
||||||
|
actions.superuser_run('udiskie', ['disable'])
|
||||||
|
|
||||||
|
|
||||||
def setup(helper, old_version=None):
|
def setup(helper, old_version=None):
|
||||||
"""Expand root parition on first setup."""
|
"""Install and configure the module."""
|
||||||
|
helper.install(managed_packages, skip_recommends=True)
|
||||||
|
helper.call('post', actions.superuser_run, 'udiskie', ['enable'])
|
||||||
|
global service
|
||||||
|
if service is None:
|
||||||
|
service = service_module.Service(
|
||||||
|
managed_services[0], name, ports=[], is_external=True,
|
||||||
|
is_enabled=is_enabled, enable=enable, disable=disable,
|
||||||
|
is_running=is_running)
|
||||||
|
helper.call('post', service.notify_enabled, None, True)
|
||||||
disks = get_disks()
|
disks = get_disks()
|
||||||
root_device = get_root_device(disks)
|
root_device = get_root_device(disks)
|
||||||
if is_expandable(root_device):
|
if is_expandable(root_device):
|
||||||
|
|||||||
@ -36,6 +36,12 @@
|
|||||||
<h2>{{ title }}</h2>
|
<h2>{{ title }}</h2>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block description %}
|
||||||
|
{% for paragraph in description %}
|
||||||
|
<p>{{ paragraph|safe }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% if manual_page %}
|
{% if manual_page %}
|
||||||
<p class="manual-page">
|
<p class="manual-page">
|
||||||
<a href="{% url 'help:manual-page' manual_page %}">
|
<a href="{% url 'help:manual-page' manual_page %}">
|
||||||
@ -44,7 +50,7 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p>{% trans "The following disks are in use:" %}</p>
|
<p>{% trans "The following storage devices are in use:" %}</p>
|
||||||
|
|
||||||
<table class="table table-bordered table-condensed table-striped">
|
<table class="table table-bordered table-condensed table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@ -66,17 +72,17 @@
|
|||||||
{% if disk.percent_used < 75 %}
|
{% if disk.percent_used < 75 %}
|
||||||
<div class="progress-bar progress-bar-striped progress-bar-success"
|
<div class="progress-bar progress-bar-striped progress-bar-success"
|
||||||
{% elif disk.percent_used < 90 %}
|
{% elif disk.percent_used < 90 %}
|
||||||
<div class="progress-bar progress-bar-striped progress-bar-warning"
|
<div class="progress-bar progress-bar-striped progress-bar-warning"
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="progress-bar progress-bar-striped progress-bar-danger"
|
<div class="progress-bar progress-bar-striped progress-bar-danger"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
role="progressbar" aria-valuenow="disk.percent_used"
|
role="progressbar" aria-valuenow="disk.percent_used"
|
||||||
aria-valuemin="0" aria-valuemax="100"
|
aria-valuemin="0" aria-valuemax="100"
|
||||||
style="width: {{ disk.percent_used }}%;">
|
style="width: {{ disk.percent_used }}%;">
|
||||||
{{ disk.percent_used }}%
|
{{ disk.percent_used }}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>{{ disk.used_str }} / {{ disk.size_str }}</div>
|
<div>{{ disk.used_str }} / {{ disk.size_str }}</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -100,4 +106,55 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% block status %}
|
||||||
|
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
<h3>{% trans "Removable Devices" %}</h3>
|
||||||
|
|
||||||
|
{% if not devices %}
|
||||||
|
<p>
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
There are no additional storage devices attached.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<table class="table table-bordered table-condensed table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Device" %}</th>
|
||||||
|
<th>{% trans "Label" %}</th>
|
||||||
|
<th>{% trans "Mount Point" %}</th>
|
||||||
|
<th>{% trans "Type" %}</th>
|
||||||
|
<th>{% trans "Size" %}</th>
|
||||||
|
<th>{% trans "Actions" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for device in devices %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ device.device }}</td>
|
||||||
|
<td>{{ device.label }}</td>
|
||||||
|
<td>{{ device.mount_points|join:', ' }}</td>
|
||||||
|
<td>{{ device.filesystem_type }}</td>
|
||||||
|
<td>{{ device.size }}</td>
|
||||||
|
<td>
|
||||||
|
{% if device.mount_points %}
|
||||||
|
<form class="form" method="post"
|
||||||
|
action="{% url 'storage:eject' device.device|urlencode:"" %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<button type="submit"
|
||||||
|
class="btn btn-sm btn-default glyphicon glyphicon-eject">
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -14,7 +14,6 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
"""
|
"""
|
||||||
URLs for the disks module.
|
URLs for the disks module.
|
||||||
"""
|
"""
|
||||||
@ -23,8 +22,9 @@ from django.conf.urls import url
|
|||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^sys/storage/$', views.index, name='index'),
|
url(r'^sys/storage/$', views.index, name='index'),
|
||||||
url(r'^sys/storage/expand$', views.expand, name='expand'),
|
url(r'^sys/storage/expand$', views.expand, name='expand'),
|
||||||
|
url(r'^sys/storage/eject/(?P<device_path>[\w%]+)/$', views.eject,
|
||||||
|
name='eject')
|
||||||
]
|
]
|
||||||
|
|||||||
@ -19,16 +19,20 @@ Views for storage module.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
from plinth.modules import storage
|
from plinth.modules import storage
|
||||||
from plinth.utils import format_lazy, is_user_admin
|
from plinth.utils import format_lazy, is_user_admin
|
||||||
|
|
||||||
|
from . import udisks2
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -44,7 +48,9 @@ def index(request):
|
|||||||
return TemplateResponse(
|
return TemplateResponse(
|
||||||
request, 'storage.html', {
|
request, 'storage.html', {
|
||||||
'title': _('Storage'),
|
'title': _('Storage'),
|
||||||
|
'description': storage.description,
|
||||||
'disks': disks,
|
'disks': disks,
|
||||||
|
'devices': udisks2.list_devices(),
|
||||||
'manual_page': storage.manual_page,
|
'manual_page': storage.manual_page,
|
||||||
'expandable_root_size': expandable_root_size
|
'expandable_root_size': expandable_root_size
|
||||||
})
|
})
|
||||||
@ -73,9 +79,10 @@ def expand_partition(request, device):
|
|||||||
try:
|
try:
|
||||||
storage.expand_partition(device)
|
storage.expand_partition(device)
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
messages.error(request,
|
messages.error(
|
||||||
_('Error expanding partition: {exception}')
|
request,
|
||||||
.format(exception=exception))
|
_('Error expanding partition: {exception}')
|
||||||
|
.format(exception=exception))
|
||||||
else:
|
else:
|
||||||
messages.success(request, _('Partition expanded successfully.'))
|
messages.success(request, _('Partition expanded successfully.'))
|
||||||
|
|
||||||
@ -106,3 +113,37 @@ def warn_about_low_disk_space(request):
|
|||||||
messages.error(request, message)
|
messages.error(request, message)
|
||||||
elif percent_used > 75 or free_gib < 2:
|
elif percent_used > 75 or free_gib < 2:
|
||||||
messages.warning(request, message)
|
messages.warning(request, message)
|
||||||
|
|
||||||
|
|
||||||
|
@require_POST
|
||||||
|
def eject(request, device_path):
|
||||||
|
"""Eject a device, given its path.
|
||||||
|
|
||||||
|
Device path is quoted with slashes written as %2F.
|
||||||
|
|
||||||
|
"""
|
||||||
|
device_path = urllib.parse.unquote(device_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
drive = udisks2.eject_drive_of_device(device_path)
|
||||||
|
if drive:
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
_('{drive_vendor} {drive_model} can be safely unplugged.')
|
||||||
|
.format(drive_vendor=drive['vendor'],
|
||||||
|
drive_model=drive['model']))
|
||||||
|
else:
|
||||||
|
messages.success(request, _('Device can be safely unplugged.'))
|
||||||
|
except Exception as exception:
|
||||||
|
try:
|
||||||
|
message = udisks2.get_error_message(exception)
|
||||||
|
except AttributeError:
|
||||||
|
message = str(exception)
|
||||||
|
|
||||||
|
logger.exception('Error ejecting device - %s', message)
|
||||||
|
messages.error(
|
||||||
|
request,
|
||||||
|
_('Error ejecting device: {error_message}').format(
|
||||||
|
error_message=message))
|
||||||
|
|
||||||
|
return redirect(reverse('storage:index'))
|
||||||
|
|||||||
@ -1,90 +0,0 @@
|
|||||||
#
|
|
||||||
# This file is part of FreedomBox.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
"""
|
|
||||||
FreedomBox app for udiskie.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from plinth import action_utils, actions
|
|
||||||
from plinth import service as service_module
|
|
||||||
from plinth.menu import main_menu
|
|
||||||
|
|
||||||
version = 1
|
|
||||||
|
|
||||||
managed_services = ['freedombox-udiskie']
|
|
||||||
|
|
||||||
managed_packages = ['udiskie', 'gir1.2-udisks-2.0']
|
|
||||||
|
|
||||||
name = _('udiskie')
|
|
||||||
|
|
||||||
short_description = _('Removable Media')
|
|
||||||
|
|
||||||
description = [
|
|
||||||
_('udiskie allows automatic mounting of removable media, such as flash '
|
|
||||||
'drives.'),
|
|
||||||
]
|
|
||||||
|
|
||||||
service = None
|
|
||||||
|
|
||||||
|
|
||||||
def init():
|
|
||||||
"""Intialize the module."""
|
|
||||||
menu = main_menu.get('system')
|
|
||||||
menu.add_urlname(name, 'glyphicon-floppy-disk', 'udiskie:index',
|
|
||||||
short_description)
|
|
||||||
|
|
||||||
global service
|
|
||||||
setup_helper = globals()['setup_helper']
|
|
||||||
if setup_helper.get_state() != 'needs-setup':
|
|
||||||
service = service_module.Service(
|
|
||||||
managed_services[0], name, ports=[], is_external=True,
|
|
||||||
is_enabled=is_enabled, enable=enable, disable=disable,
|
|
||||||
is_running=is_running)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(helper, old_version=None):
|
|
||||||
"""Install and configure the module."""
|
|
||||||
helper.install(managed_packages, skip_recommends=True)
|
|
||||||
helper.call('post', actions.superuser_run, 'udiskie', ['enable'])
|
|
||||||
global service
|
|
||||||
if service is None:
|
|
||||||
service = service_module.Service(
|
|
||||||
managed_services[0], name, ports=[], is_external=True,
|
|
||||||
is_enabled=is_enabled, enable=enable, disable=disable,
|
|
||||||
is_running=is_running)
|
|
||||||
helper.call('post', service.notify_enabled, None, True)
|
|
||||||
|
|
||||||
|
|
||||||
def is_running():
|
|
||||||
"""Return whether the service is running."""
|
|
||||||
return action_utils.service_is_running('freedombox-udiskie')
|
|
||||||
|
|
||||||
|
|
||||||
def is_enabled():
|
|
||||||
"""Return whether the module is enabled."""
|
|
||||||
return action_utils.service_is_enabled('freedombox-udiskie')
|
|
||||||
|
|
||||||
|
|
||||||
def enable():
|
|
||||||
"""Enable the module."""
|
|
||||||
actions.superuser_run('udiskie', ['enable'])
|
|
||||||
|
|
||||||
|
|
||||||
def disable():
|
|
||||||
"""Disable the module."""
|
|
||||||
actions.superuser_run('udiskie', ['disable'])
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
{% extends "service.html" %}
|
|
||||||
{% comment %}
|
|
||||||
#
|
|
||||||
# This file is part of FreedomBox.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
{% endcomment %}
|
|
||||||
|
|
||||||
{% load bootstrap %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block status %}
|
|
||||||
|
|
||||||
{{ block.super }}
|
|
||||||
|
|
||||||
<h3>{% trans "Devices" %}</h3>
|
|
||||||
|
|
||||||
{% if not devices %}
|
|
||||||
<p>
|
|
||||||
{% blocktrans trimmed %}
|
|
||||||
There are no additional storage devices attached.
|
|
||||||
{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
{% else %}
|
|
||||||
<table class="table table-bordered table-condensed table-striped">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{% trans "Device" %}</th>
|
|
||||||
<th>{% trans "Label" %}</th>
|
|
||||||
<th>{% trans "Size" %}</th>
|
|
||||||
<th>{% trans "Filesystem" %}</th>
|
|
||||||
<th>{% trans "Mount Point" %}</th>
|
|
||||||
<th>{% trans "Actions" %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for device in devices %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ device.device }}</td>
|
|
||||||
<td>{{ device.label }}</td>
|
|
||||||
<td>{{ device.size }}</td>
|
|
||||||
<td>{{ device.filesystem_type }}</td>
|
|
||||||
<td>{{ device.mount_points|join:', ' }}</td>
|
|
||||||
<td>
|
|
||||||
{% if device.mount_points %}
|
|
||||||
<form class="form" method="post"
|
|
||||||
action="{% url 'udiskie:eject' device.device|urlencode:"" %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
<button type="submit"
|
|
||||||
class="btn btn-sm btn-default glyphicon glyphicon-eject">
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
#
|
|
||||||
# This file is part of FreedomBox.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
"""
|
|
||||||
URLs for the udiskie module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
|
||||||
|
|
||||||
from plinth.modules import udiskie
|
|
||||||
|
|
||||||
from .views import Index, eject
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(
|
|
||||||
r'^sys/udiskie/$',
|
|
||||||
Index.as_view(service_id=udiskie.managed_services[0],
|
|
||||||
description=udiskie.description, show_status_block=True),
|
|
||||||
name='index'),
|
|
||||||
url(r'^sys/udiskie/eject/(?P<device_path>[\w%]+)/$', eject, name='eject'),
|
|
||||||
]
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
#
|
|
||||||
# This file is part of FreedomBox.
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
"""
|
|
||||||
Views for udiskie module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import urllib.parse
|
|
||||||
from logging import Logger
|
|
||||||
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.views.decorators.http import require_POST
|
|
||||||
|
|
||||||
from plinth.views import ServiceView
|
|
||||||
|
|
||||||
from . import udisks2
|
|
||||||
|
|
||||||
logger = Logger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Index(ServiceView):
|
|
||||||
"""View to show devices."""
|
|
||||||
template_name = 'udiskie.html'
|
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
|
||||||
"""Return the context data rendering the template."""
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['devices'] = udisks2.list_devices()
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
|
||||||
def eject(request, device_path):
|
|
||||||
"""Eject a device, given its path.
|
|
||||||
|
|
||||||
Device path is quoted with slashes written as %2F.
|
|
||||||
|
|
||||||
"""
|
|
||||||
device_path = urllib.parse.unquote(device_path)
|
|
||||||
|
|
||||||
try:
|
|
||||||
drive = udisks2.eject_drive_of_device(device_path)
|
|
||||||
if drive:
|
|
||||||
messages.success(
|
|
||||||
request,
|
|
||||||
_('{drive_vendor} {drive_model} can be safely unplugged.')
|
|
||||||
.format(drive_vendor=drive['vendor'],
|
|
||||||
drive_model=drive['model']))
|
|
||||||
else:
|
|
||||||
messages.success(request, _('Device can be safely unplugged.'))
|
|
||||||
except Exception as exception:
|
|
||||||
try:
|
|
||||||
message = udisks2.get_error_message(exception)
|
|
||||||
except AttributeError:
|
|
||||||
message = str(exception)
|
|
||||||
|
|
||||||
logger.exception('Error ejecting device - %s', message)
|
|
||||||
messages.error(
|
|
||||||
request,
|
|
||||||
_('Error ejecting device: {error_message}').format(
|
|
||||||
error_message=message))
|
|
||||||
|
|
||||||
return redirect(reverse('udiskie:index'))
|
|
||||||
Loading…
x
Reference in New Issue
Block a user