diff --git a/actions/backups b/actions/backups index 1a37e213c..c80719127 100755 --- a/actions/backups +++ b/actions/backups @@ -45,7 +45,7 @@ def parse_arguments(): 'setup', help='Create repository if it does not already exist') init = subparsers.add_parser('init', help='Initialize a repository') - init.add_argument('--encryption', help='Enryption of the repository', + init.add_argument('--encryption', help='Encryption of the repository', required=True) info = subparsers.add_parser('info', help='Show repository information') @@ -151,7 +151,6 @@ def _extract(archive_path, destination, locations=None, env=None): """Extract archive contents.""" if not env: env = dict(os.environ) - # TODO: is LANG necessary? env['LANG'] = 'C.UTF-8' prev_dir = os.getcwd() diff --git a/actions/sshfs b/actions/sshfs index c0a4771a4..11077b584 100755 --- a/actions/sshfs +++ b/actions/sshfs @@ -48,8 +48,8 @@ def parse_arguments(): help='unmount an ssh filesystem') umount.add_argument('--mountpoint', help='Mountpoint to unmount', required=True) - is_mounted = subparsers.add_parser('is-mounted', - help='Check whether an sshfs is mouned') + is_mounted = subparsers.add_parser( + 'is-mounted', help='Check whether a mountpoint is mounted') is_mounted.add_argument('--mountpoint', help='Mountpoint to check', required=True) @@ -83,7 +83,7 @@ def subcommand_mount(arguments): def subcommand_umount(arguments): - """Show repository information.""" + """Unmount a mountpoint.""" run(['umount', arguments.mountpoint]) diff --git a/plinth/modules/backups/errors.py b/plinth/modules/backups/errors.py index e7d88d4cd..e7e13b534 100644 --- a/plinth/modules/backups/errors.py +++ b/plinth/modules/backups/errors.py @@ -26,3 +26,8 @@ class BorgError(PlinthError): class BorgRepositoryDoesNotExistError(BorgError): """Borg access to a repository works but the repository does not exist""" pass + + +class SshfsError(PlinthError): + """Generic sshfs errors""" + pass diff --git a/plinth/modules/backups/forms.py b/plinth/modules/backups/forms.py index b38030648..d4d2205c1 100644 --- a/plinth/modules/backups/forms.py +++ b/plinth/modules/backups/forms.py @@ -142,8 +142,9 @@ class AddRepositoryForm(forms.Form): path = cleaned_data.get("repository") credentials = self.get_credentials() - self.repository = SshBorgRepository(path=path, credentials=credentials) try: + self.repository = SshBorgRepository(path=path, + credentials=credentials) self.repository.get_info() except BorgRepositoryDoesNotExistError: pass diff --git a/plinth/modules/backups/repository.py b/plinth/modules/backups/repository.py index f139c9c3d..861df2784 100644 --- a/plinth/modules/backups/repository.py +++ b/plinth/modules/backups/repository.py @@ -21,6 +21,7 @@ Remote and local Borg backup repositories import json import logging import os +from uuid import uuid1 from django.utils.translation import ugettext_lazy as _ @@ -30,7 +31,7 @@ from plinth.errors import ActionError from . import api, network_storage, _backup_handler, ROOT_REPOSITORY_NAME, \ ROOT_REPOSITORY_UUID, ROOT_REPOSITORY, restore_archive_handler, \ zipstream -from .errors import BorgError, BorgRepositoryDoesNotExistError +from .errors import BorgError, BorgRepositoryDoesNotExistError, SshfsError logger = logging.getLogger(__name__) @@ -53,6 +54,17 @@ KNOWN_ERRORS = [{ "errors": ["not a valid repository", "does not exist"], "message": _("Repository not found"), "raise_as": BorgRepositoryDoesNotExistError, + }, + { + "errors": [("passphrase supplied in BORG_PASSPHRASE or by " + "BORG_PASSCOMMAND is incorrect")], + "message": _("Incorrect encryption passphrase"), + "raise_as": BorgError, + }, + { + "errors": [("Connection reset by peer")], + "message": _("SSH access denied"), + "raise_as": SshfsError, }] @@ -192,23 +204,23 @@ class SshBorgRepository(BorgRepository): Provide a uuid to instanciate an existing repository, or 'ssh_path' and 'credentials' for a new repository. """ - if uuid: - self.uuid = uuid - # If all data are given, instanciate right away. - if path and credentials: - self._path = path - self.credentials = credentials - else: - self._load_from_kvstore() - # No uuid given: new instance. - elif path and credentials: + is_new_instance = not bool(uuid) + if not uuid: + uuid = str(uuid1()) + self.uuid = uuid + + if path and credentials: self._path = path self.credentials = credentials else: - raise ValueError('Invalid arguments.') + if is_new_instance: + # Either a uuid, or both a path and credentials must be given + raise ValueError('Invalid arguments.') + else: + self._load_from_kvstore() + if automount: - if self.uuid and not self.is_mounted: - self.mount() + self.mount() @property def repo_path(self): @@ -273,6 +285,9 @@ class SshBorgRepository(BorgRepository): self.uuid = network_storage.update_or_add(storage) def mount(self): + if self.is_mounted: + return + arguments = ['mount', '--mountpoint', self.mountpoint, '--path', self._path] arguments, kwargs = self._append_sshfs_arguments(arguments, @@ -280,6 +295,8 @@ class SshBorgRepository(BorgRepository): self._run('sshfs', arguments, kwargs=kwargs) def umount(self): + if not self.is_mounted: + return self._run('sshfs', ['umount', '--mountpoint', self.mountpoint]) def remove_repository(self): diff --git a/plinth/modules/backups/templates/backups_repository_add.html b/plinth/modules/backups/templates/backups_repository_add.html index e5c31adcb..db2977294 100644 --- a/plinth/modules/backups/templates/backups_repository_add.html +++ b/plinth/modules/backups/templates/backups_repository_add.html @@ -31,10 +31,11 @@ {{ form|bootstrap }} - + value="{% trans "Create Repository" %}"/> + + {% trans "Abort" %} + {% endblock %} diff --git a/plinth/modules/backups/templates/backups_repository_remove.html b/plinth/modules/backups/templates/backups_repository_remove.html index 6513f1cb2..8329a328e 100644 --- a/plinth/modules/backups/templates/backups_repository_remove.html +++ b/plinth/modules/backups/templates/backups_repository_remove.html @@ -25,11 +25,12 @@

{{ title }}

- {% trans "Are you sure that you want to remove the repository" %}
- - {{ repository.path }}? - + {% trans "Are you sure that you want to remove this repository?" %}

+

+ {{ repository.name }} +

+

{% blocktrans %} The remote repository will not be deleted. This just removes the repository from the listing on the backup page, you @@ -42,9 +43,7 @@ {% csrf_token %} + value="{% trans "Remove Repository" %}"/> {% trans "Abort" %} diff --git a/plinth/modules/backups/templates/backups_repository_test.html b/plinth/modules/backups/templates/backups_repository_test.html deleted file mode 100644 index da26fd960..000000000 --- a/plinth/modules/backups/templates/backups_repository_test.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "base.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 . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block content %} - -

{{ title }}

- - {{ message }} - - -{% endblock %} diff --git a/plinth/modules/backups/urls.py b/plinth/modules/backups/urls.py index bfdb83e27..e91150891 100644 --- a/plinth/modules/backups/urls.py +++ b/plinth/modules/backups/urls.py @@ -23,7 +23,7 @@ from django.conf.urls import url from .views import IndexView, CreateArchiveView, AddRepositoryView, \ DeleteArchiveView, DownloadArchiveView, RemoveRepositoryView, \ mount_repository, umount_repository, UploadArchiveView, \ - RestoreArchiveView, RestoreFromUploadView, TestRepositoryView + RestoreArchiveView, RestoreFromUploadView urlpatterns = [ url(r'^sys/backups/$', IndexView.as_view(), name='index'), @@ -39,8 +39,6 @@ urlpatterns = [ RestoreFromUploadView.as_view(), name='restore-from-upload'), url(r'^sys/backups/repositories/add$', AddRepositoryView.as_view(), name='repository-add'), - url(r'^sys/backups/repositories/test/$', - TestRepositoryView.as_view(), name='repository-test'), url(r'^sys/backups/repositories/delete/(?P[^/]+)/$', RemoveRepositoryView.as_view(), name='repository-remove'), url(r'^sys/backups/repositories/mount/(?P[^/]+)/$', diff --git a/plinth/modules/backups/views.py b/plinth/modules/backups/views.py index cc26ac4e6..7fb34e2dc 100644 --- a/plinth/modules/backups/views.py +++ b/plinth/modules/backups/views.py @@ -35,14 +35,14 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy from django.views.generic import View, FormView, TemplateView -from plinth.errors import PlinthError, ActionError +from plinth.errors import PlinthError from plinth.modules import backups, storage from . import api, forms, SESSION_PATH_VARIABLE, ROOT_REPOSITORY from .repository import BorgRepository, SshBorgRepository, get_repository, \ get_ssh_repositories from .decorators import delete_tmp_backup_file -from .errors import BorgError, BorgRepositoryDoesNotExistError +from .errors import BorgRepositoryDoesNotExistError logger = logging.getLogger(__name__) @@ -235,7 +235,6 @@ class RestoreArchiveView(BaseRestoreView): def form_valid(self, form): """Restore files from the archive on valid form submission.""" repository = get_repository(self.kwargs['uuid']) - import ipdb; ipdb.set_trace() repository.restore_archive(self.kwargs['name'], form.cleaned_data['selected_apps']) return super().form_valid(form) @@ -279,30 +278,6 @@ class AddRepositoryView(SuccessMessageMixin, FormView): return super().form_valid(form) -class TestRepositoryView(TemplateView): - """View to create a new repository.""" - template_name = 'backups_repository_test.html' - - def post(self, request): - # TODO: add support for borg encryption and ssh keyfile - context = self.get_context_data() - credentials = { - 'ssh_password': request.POST['backups-ssh_password'], - } - repository = SshBorgRepository(path=request.POST['backups-repository'], - credentials=credentials) - - try: - repo_info = repository.get_info() - context["message"] = repo_info - except BorgError as err: - context["error"] = str(err) - except ActionError as err: - context["error"] = str(err) - - return self.render_to_response(context) - - class RemoveRepositoryView(SuccessMessageMixin, TemplateView): """View to delete a repository.""" template_name = 'backups_repository_remove.html'