mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-09-19 04:59:01 +00:00
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:
parent
984b7dca88
commit
661a00198e
@ -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]
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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])
|
||||||
|
|
||||||
|
|||||||
@ -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')
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user