backups: Improve UX of adding ssh remote

- Create empty directory if not exists
- Check if the directory is empty
  - if not empty, check if it's an existing backup repository
  - else throw an error

Signed-off-by: Joseph Nuthalapati <njoseph@thoughtworks.com>
This commit is contained in:
Joseph Nuthalapati 2019-05-16 16:40:02 +05:30
parent 10cf3ee536
commit e618e08fe7
No known key found for this signature in database
GPG Key ID: 5398F00A2FA43C35
2 changed files with 44 additions and 7 deletions

View File

@ -31,9 +31,9 @@ from plinth.utils import format_lazy
from . import api from . import api
version = 2 version = 3
managed_packages = ['borgbackup', 'sshfs'] managed_packages = ['borgbackup', 'sshfs', 'python3-paramiko']
name = _('Backups') name = _('Backups')

View File

@ -18,6 +18,8 @@
Forms for backups module. Forms for backups module.
""" """
import logging
import paramiko
from django import forms from django import forms
from django.core.validators import FileExtensionValidator from django.core.validators import FileExtensionValidator
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
@ -28,6 +30,8 @@ from . import api, network_storage, ROOT_REPOSITORY_NAME
from .errors import BorgRepositoryDoesNotExistError from .errors import BorgRepositoryDoesNotExistError
from .repository import SshBorgRepository from .repository import SshBorgRepository
logger = logging.getLogger(__name__)
def _get_app_choices(apps): def _get_app_choices(apps):
"""Return a list of check box multiple choices from list of apps.""" """Return a list of check box multiple choices from list of apps."""
@ -130,6 +134,14 @@ class AddRepositoryForm(forms.Form):
return credentials return credentials
def clean(self): def clean(self):
"""
Validation of SSH remote
* Create empty directory if not exists
* Check if the directory is empty
- if not empty, check if it's an existing backup repository
- else throw an error
"""
cleaned_data = super(AddRepositoryForm, self).clean() cleaned_data = super(AddRepositoryForm, self).clean()
passphrase = cleaned_data.get("encryption_passphrase") passphrase = cleaned_data.get("encryption_passphrase")
confirm_passphrase = cleaned_data.get("confirm_encryption_passphrase") confirm_passphrase = cleaned_data.get("confirm_encryption_passphrase")
@ -141,12 +153,37 @@ class AddRepositoryForm(forms.Form):
path = cleaned_data.get("repository") path = cleaned_data.get("repository")
credentials = self.get_credentials() credentials = self.get_credentials()
# Validate remote
user_at_host, dir_path = path.split(':')
username, hostname = user_at_host.split('@')
dir_path = dir_path.replace('~', f'/home/{username}')
password = credentials['ssh_password']
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try: try:
self.repository = SshBorgRepository(path=path, ssh_client.connect(hostname, username=username, password=password)
credentials=credentials)
self.repository.get_info()
except BorgRepositoryDoesNotExistError:
pass
except Exception as err: except Exception as err:
msg = _('Accessing the remote repository failed. Details: %(err)s') msg = _('Accessing the remote repository failed. Details: %(err)s')
raise forms.ValidationError(msg, params={'err': str(err)}) raise forms.ValidationError(msg, params={'err': str(err)})
else:
with ssh_client.open_sftp() as sftp_client:
try:
dir_contents = sftp_client.listdir(dir_path)
except FileNotFoundError:
logger.info(
_(f"Directory {dir_path} doesn't exist. Creating ..."))
sftp_client.mkdir(dir_path)
else:
if dir_contents:
try:
self.repository = SshBorgRepository(
path=path, credentials=credentials)
self.repository.get_info()
except BorgRepositoryDoesNotExistError:
msg = _(f'Directory {path.split(":")[-1]} is '
'neither empty nor is an existing '
'backups repository.')
raise forms.ValidationError(msg)
finally:
ssh_client.close()