backups: Minor styling changes

- Add docstrings.

- Add blank lines for extra readability.

- Use <span> instead of <i> tag for icon in template.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Joseph Nuthalapati <njoseph@thoughtworks.com>
This commit is contained in:
Sunil Mohan Adapa 2019-06-24 16:46:23 -07:00 committed by Joseph Nuthalapati
parent 984b7dca88
commit 661a00198e
No known key found for this signature in database
GPG Key ID: 5398F00A2FA43C35
6 changed files with 30 additions and 9 deletions

View File

@ -148,7 +148,9 @@ def restore_from_upload(path, apps=None):
def is_ssh_hostkey_verified(hostname): def is_ssh_hostkey_verified(hostname):
"""Check whether SSH Hostkey has already been verified. """Check whether SSH Hostkey has already been verified.
hostname: Domain name or IP address of the host hostname: Domain name or IP address of the host
""" """
known_hosts_path = cfg.known_hosts known_hosts_path = cfg.known_hosts
if not os.path.exists(known_hosts_path): if not os.path.exists(known_hosts_path):
@ -164,5 +166,6 @@ def split_path(path):
Network interface information is kept in the hostname if provided. Network interface information is kept in the hostname if provided.
e.g. fe80::2078:6c26:498a:1fa5%wlp1s0 e.g. fe80::2078:6c26:498a:1fa5%wlp1s0
""" """
return re.findall(r'^(.*)@([^/]*):(.*)$', path)[0] return re.findall(r'^(.*)@([^/]*):(.*)$', path)[0]

View File

@ -59,6 +59,7 @@ def _get_repository_choices():
for storage in storages.values(): for storage in storages.values():
if storage['verified']: if storage['verified']:
choices += [(storage['uuid'], storage['path'])] choices += [(storage['uuid'], storage['path'])]
return choices return choices
@ -99,6 +100,7 @@ class UploadForm(forms.Form):
def repository_validator(path): def repository_validator(path):
"""Validate an SSH repository path."""
if not ('@' in path and ':' in path): if not ('@' in path and ':' in path):
raise ValidationError(_('Repository path format incorrect.')) raise ValidationError(_('Repository path format incorrect.'))
@ -125,6 +127,7 @@ def repository_validator(path):
class AddRepositoryForm(forms.Form): class AddRepositoryForm(forms.Form):
"""Form to add new SSH remote repository."""
repository = forms.CharField( repository = forms.CharField(
label=_('SSH Repository Path'), strip=True, label=_('SSH Repository Path'), strip=True,
help_text=_('Path of a new or existing repository. Example: ' help_text=_('Path of a new or existing repository. Example: '
@ -161,19 +164,23 @@ class AddRepositoryForm(forms.Form):
return self.cleaned_data return self.cleaned_data
def clean_repository(self): def clean_repository(self):
"""Validate repository form field."""
path = self.cleaned_data.get('repository') path = self.cleaned_data.get('repository')
# Avoid creation of duplicate ssh remotes # Avoid creation of duplicate ssh remotes
self._check_if_duplicate_remote(path) self._check_if_duplicate_remote(path)
return path return path
def _check_if_duplicate_remote(self, path): @staticmethod
for ns in network_storage.get_storages().values(): def _check_if_duplicate_remote(path):
if ns['path'] == path: """Raise validation error if given path is a stored remote."""
for storage in network_storage.get_storages().values():
if storage['path'] == path:
raise forms.ValidationError( raise forms.ValidationError(
_('Remote backup repository already exists.')) _('Remote backup repository already exists.'))
class VerifySshHostkeyForm(forms.Form): class VerifySshHostkeyForm(forms.Form):
"""Form to verify the SSH public key for a host."""
ssh_public_key = forms.ChoiceField( ssh_public_key = forms.ChoiceField(
label=_('Select verified SSH public key'), widget=forms.RadioSelect) label=_('Select verified SSH public key'), widget=forms.RadioSelect)
@ -184,9 +191,9 @@ class VerifySshHostkeyForm(forms.Form):
self.fields['ssh_public_key'].choices = self._get_all_public_keys( self.fields['ssh_public_key'].choices = self._get_all_public_keys(
hostname) hostname)
def _get_all_public_keys(self, hostname): @staticmethod
"""Use ssh-keyscan to get all the SSH public keys of the def _get_all_public_keys(hostname):
given hostname.""" """Use ssh-keyscan to get all the SSH public keys of a host."""
# Fetch public keys of ssh remote # Fetch public keys of ssh remote
res1 = subprocess.run(['ssh-keyscan', hostname], res1 = subprocess.run(['ssh-keyscan', hostname],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,

View File

@ -311,6 +311,7 @@ class SshBorgRepository(BorgRepository):
"""Initialize / create a borg repository.""" """Initialize / create a borg repository."""
if encryption not in SUPPORTED_BORG_ENCRYPTION: if encryption not in SUPPORTED_BORG_ENCRYPTION:
raise ValueError('Unsupported encryption: %s' % encryption) raise ValueError('Unsupported encryption: %s' % encryption)
self.run( self.run(
['init', '--path', self.repo_path, '--encryption', encryption]) ['init', '--path', self.repo_path, '--encryption', encryption])

View File

@ -264,7 +264,7 @@ class DownloadArchiveView(View):
class AddRepositoryView(SuccessMessageMixin, FormView): class AddRepositoryView(SuccessMessageMixin, FormView):
"""View to verify the SSH Hostkey of the server and save repository.""" """View to create a new remote backup repository."""
form_class = forms.AddRepositoryForm form_class = forms.AddRepositoryForm
template_name = 'backups_repository_add.html' template_name = 'backups_repository_add.html'
success_url = reverse_lazy('backups:index') success_url = reverse_lazy('backups:index')
@ -319,6 +319,7 @@ class VerifySshHostkeyView(SuccessMessageMixin, FormView):
if not self.repo_data: if not self.repo_data:
uuid = self.kwargs['uuid'] uuid = self.kwargs['uuid']
self.repo_data = network_storage.get(uuid) self.repo_data = network_storage.get(uuid)
return self.repo_data return self.repo_data
def _get_hostname(self): def _get_hostname(self):
@ -346,6 +347,7 @@ class VerifySshHostkeyView(SuccessMessageMixin, FormView):
known_hosts_file.write('\n') known_hosts_file.write('\n')
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
"""Skip this view if host is already verified."""
if is_ssh_hostkey_verified(self._get_hostname()): if is_ssh_hostkey_verified(self._get_hostname()):
self._add_remote_repository() self._add_remote_repository()
messages.success(self.request, messages.success(self.request,
@ -363,6 +365,7 @@ class VerifySshHostkeyView(SuccessMessageMixin, FormView):
return super().form_valid(form) return super().form_valid(form)
def _add_remote_repository(self): def _add_remote_repository(self):
"""On successful verification of host, add repository."""
repo_data = self._get_repo_data() repo_data = self._get_repo_data()
path = repo_data['path'] path = repo_data['path']
credentials = repo_data['credentials'] credentials = repo_data['credentials']
@ -476,21 +479,26 @@ class RemoveRepositoryView(SuccessMessageMixin, TemplateView):
messages.success( messages.success(
request, request,
_('Repository removed. The remote backup itself was not deleted.')) _('Repository removed. The remote backup itself was not deleted.'))
return redirect('backups:index') return redirect('backups:index')
def umount_repository(request, uuid): def umount_repository(request, uuid):
"""View to unmount a remote SSH repository."""
repository = SshBorgRepository(uuid=uuid) repository = SshBorgRepository(uuid=uuid)
repository.umount() repository.umount()
if repository.is_mounted: if repository.is_mounted:
messages.error(request, _('Unmounting failed!')) messages.error(request, _('Unmounting failed!'))
return redirect('backups:index') return redirect('backups:index')
def mount_repository(request, uuid): def mount_repository(request, uuid):
"""View to mount a remote SSH repository."""
# Do not mount unverified ssh repositories. Prompt for verification. # Do not mount unverified ssh repositories. Prompt for verification.
if not network_storage.get(uuid).get('verified'): if not network_storage.get(uuid).get('verified'):
return redirect('backups:verify-ssh-hostkey', uuid=uuid) return redirect('backups:verify-ssh-hostkey', uuid=uuid)
repository = SshBorgRepository(uuid=uuid) repository = SshBorgRepository(uuid=uuid)
try: try:
repository.mount() repository.mount()
@ -500,4 +508,5 @@ def mount_repository(request, uuid):
else: else:
if not repository.is_mounted: if not repository.is_mounted:
messages.error(request, _('Mounting failed')) messages.error(request, _('Mounting failed'))
return redirect('backups:index') return redirect('backups:index')

View File

@ -25,7 +25,8 @@
<p> <p>
<button type="button" class="btn btn-default collapsed collapsible-button" <button type="button" class="btn btn-default collapsed collapsible-button"
data-toggle="collapse" data-target="#clients"> data-toggle="collapse" data-target="#clients">
<i class="fa fa-chevron-right fa-fw" aria-hidden="true"></i> {% trans "Client Apps" %} <span class="fa fa-chevron-right fa-fw" aria-hidden="true"></span>
{% trans "Client Apps" %}
</button> </button>
</p> </p>

View File

@ -244,7 +244,7 @@ footer {
content: "\f054"; content: "\f054";
} }
/* No-JS fallbacks for collapsible content in clients.html */ /* No-JS fallbacks for collapsible content */
.no-js .collapse { .no-js .collapse {
display: block; display: block;
} }