mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-28 08:03:36 +00:00
backups: Save new backup location to plinth database
Signed-off-by: Joseph Nuthalapati <njoseph@riseup.net> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
7c7ad6d56a
commit
de4a019063
@ -81,6 +81,7 @@ KNOWN_ERRORS = [{
|
||||
class BaseBorgRepository(ABC):
|
||||
"""Base class for all kinds of Borg repositories."""
|
||||
uuid = None
|
||||
is_mounted = True
|
||||
|
||||
def __init__(self, uuid=None, path=None, credentials=None, **kwargs):
|
||||
"""
|
||||
@ -262,6 +263,27 @@ class BaseBorgRepository(ABC):
|
||||
create_subvolume=False, backup_file=archive_path,
|
||||
encryption_passphrase=passphrase)
|
||||
|
||||
def _get_network_storage_format(self, store_credentials, verified):
|
||||
storage = {
|
||||
'path': self._path,
|
||||
'storage_type': self.storage_type,
|
||||
'added_by_module': 'backups',
|
||||
'verified': verified
|
||||
}
|
||||
if self.uuid:
|
||||
storage['uuid'] = self.uuid
|
||||
if store_credentials:
|
||||
storage['credentials'] = self.credentials
|
||||
return storage
|
||||
|
||||
def save(self, store_credentials=True, verified=False):
|
||||
"""
|
||||
Save the repository in network_storage (kvstore).
|
||||
- store_credentials: Boolean whether credentials should be stored.
|
||||
"""
|
||||
storage = self._get_network_storage_format(store_credentials, verified)
|
||||
self.uuid = network_storage.update_or_add(storage)
|
||||
|
||||
|
||||
class RootBorgRepository(BaseBorgRepository):
|
||||
"""Borg repository on the root filesystem."""
|
||||
@ -287,20 +309,16 @@ class BorgRepository(BaseBorgRepository):
|
||||
KNOWN_CREDENTIALS = ['encryption_passphrase']
|
||||
storage_type = 'disk'
|
||||
|
||||
@property
|
||||
def is_mounted(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
raise NotImplementedError
|
||||
# TODO Use disk label as the name
|
||||
# Also, name isn't being used yet
|
||||
return self.repo_path
|
||||
|
||||
@property
|
||||
def repo_path(self):
|
||||
"""
|
||||
Return the path to use for backups actions.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
def remove_repository(self):
|
||||
"""Remove a repository from the kvstore and delete its mountpoint"""
|
||||
self.umount()
|
||||
network_storage.delete(self.uuid)
|
||||
|
||||
|
||||
class SshBorgRepository(BaseBorgRepository):
|
||||
@ -331,27 +349,6 @@ class SshBorgRepository(BaseBorgRepository):
|
||||
['is-mounted', '--mountpoint', self.mountpoint])
|
||||
return json.loads(output)
|
||||
|
||||
def _get_network_storage_format(self, store_credentials, verified):
|
||||
storage = {
|
||||
'path': self._path,
|
||||
'storage_type': self.storage_type,
|
||||
'added_by_module': 'backups',
|
||||
'verified': verified
|
||||
}
|
||||
if self.uuid:
|
||||
storage['uuid'] = self.uuid
|
||||
if store_credentials:
|
||||
storage['credentials'] = self.credentials
|
||||
return storage
|
||||
|
||||
def save(self, store_credentials=True, verified=False):
|
||||
"""
|
||||
Save the repository in network_storage (kvstore).
|
||||
- store_credentials: Boolean whether credentials should be stored.
|
||||
"""
|
||||
storage = self._get_network_storage_format(store_credentials, verified)
|
||||
self.uuid = network_storage.update_or_add(storage)
|
||||
|
||||
def mount(self):
|
||||
if self.is_mounted:
|
||||
return
|
||||
@ -400,19 +397,43 @@ class SshBorgRepository(BaseBorgRepository):
|
||||
return (arguments, kwargs)
|
||||
|
||||
|
||||
def get_ssh_repositories():
|
||||
def get_repositories(storage_type):
|
||||
"""Get all repositories of a given storage type."""
|
||||
if storage_type == 'disk':
|
||||
return _get_disk_repositories()
|
||||
|
||||
return _get_ssh_repositories()
|
||||
|
||||
|
||||
def _get_ssh_repositories():
|
||||
"""Get all SSH Repositories including the archive content"""
|
||||
repositories = {}
|
||||
for storage in network_storage.get_storages().values():
|
||||
repository = SshBorgRepository(**storage)
|
||||
repositories[storage['uuid']] = repository.get_view_content()
|
||||
if storage['storage_type'] == 'ssh':
|
||||
repository = SshBorgRepository(**storage)
|
||||
repositories[storage['uuid']] = repository.get_view_content()
|
||||
|
||||
return repositories
|
||||
|
||||
|
||||
def get_repository(uuid):
|
||||
"""Get a local or SSH repository object instance."""
|
||||
def _get_disk_repositories():
|
||||
"""Get all disk repositories including the archive content"""
|
||||
repositories = {}
|
||||
for storage in network_storage.get_storages().values():
|
||||
if storage['storage_type'] == 'disk':
|
||||
repository = BorgRepository(**storage)
|
||||
repositories[storage['uuid']] = repository.get_view_content()
|
||||
|
||||
return repositories
|
||||
|
||||
|
||||
def create_repository(uuid):
|
||||
"""Create a local or SSH repository object instance."""
|
||||
if uuid == ROOT_REPOSITORY_UUID:
|
||||
return RootBorgRepository(path=ROOT_REPOSITORY)
|
||||
|
||||
return SshBorgRepository(uuid=uuid)
|
||||
storage = network_storage.get(uuid)
|
||||
if storage['storage_type'] == 'ssh':
|
||||
return SshBorgRepository(uuid=uuid)
|
||||
else:
|
||||
return BorgRepository(uuid=uuid)
|
||||
|
||||
@ -60,10 +60,14 @@
|
||||
|
||||
<h3>{% trans 'Existing Backups' %}</h3>
|
||||
|
||||
{% include "backups_repository.inc" with repository=root_repository uuid='root' editable=False %}
|
||||
{% include "backups_repository.inc" with repository=root_repository uuid='root' storage_type='root' editable=False %}
|
||||
|
||||
{% for uuid,repository in ssh_repositories.items %}
|
||||
{% include "backups_repository.inc" with editable=True %}
|
||||
{% include "backups_repository.inc" with editable=True storage_type='ssh' %}
|
||||
{% endfor %}
|
||||
|
||||
{% for uuid,repository in disk_repositories.items %}
|
||||
{% include "backups_repository.inc" with editable=True storage_type='disk' %}
|
||||
{% endfor %}
|
||||
|
||||
<a title="{% trans 'Add a backup location' %}"
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
<span class="pull-right">
|
||||
{% if editable %}
|
||||
|
||||
{% if storage_type != 'disk' %}
|
||||
{% if repository.mounted %}
|
||||
|
||||
<!-- With GET redirects, the browser URL points to the
|
||||
@ -62,6 +63,7 @@
|
||||
</form>
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<a title="{% trans 'Remove Backup Location. This will not delete the remote backup.' %}"
|
||||
role="button" class="repository-remove btn btn-sm btn-default"
|
||||
|
||||
@ -43,8 +43,8 @@ from . import (ROOT_REPOSITORY, SESSION_PATH_VARIABLE, api, forms,
|
||||
split_path)
|
||||
from .decorators import delete_tmp_backup_file
|
||||
from .errors import BorgRepositoryDoesNotExistError
|
||||
from .repository import (RootBorgRepository, SshBorgRepository, get_repository,
|
||||
get_ssh_repositories)
|
||||
from .repository import (BorgRepository, RootBorgRepository, SshBorgRepository,
|
||||
create_repository, get_repositories)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -62,7 +62,8 @@ class IndexView(TemplateView):
|
||||
context['manual_page'] = backups.manual_page
|
||||
root_repository = RootBorgRepository(path=ROOT_REPOSITORY)
|
||||
context['root_repository'] = root_repository.get_view_content()
|
||||
context['ssh_repositories'] = get_ssh_repositories()
|
||||
context['ssh_repositories'] = get_repositories('ssh')
|
||||
context['disk_repositories'] = get_repositories('disk')
|
||||
return context
|
||||
|
||||
|
||||
@ -82,7 +83,7 @@ class CreateArchiveView(SuccessMessageMixin, FormView):
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Create the archive on valid form submission."""
|
||||
repository = get_repository(form.cleaned_data['repository'])
|
||||
repository = create_repository(form.cleaned_data['repository'])
|
||||
if hasattr(repository, 'mount'):
|
||||
repository.mount()
|
||||
|
||||
@ -100,7 +101,7 @@ class DeleteArchiveView(SuccessMessageMixin, TemplateView):
|
||||
"""Return additional context for rendering the template."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _('Delete Archive')
|
||||
repository = get_repository(self.kwargs['uuid'])
|
||||
repository = create_repository(self.kwargs['uuid'])
|
||||
context['archive'] = repository.get_archive(self.kwargs['name'])
|
||||
if context['archive'] is None:
|
||||
raise Http404
|
||||
@ -109,7 +110,7 @@ class DeleteArchiveView(SuccessMessageMixin, TemplateView):
|
||||
|
||||
def post(self, request, uuid, name):
|
||||
"""Delete the archive."""
|
||||
repository = get_repository(uuid)
|
||||
repository = create_repository(uuid)
|
||||
repository.delete_archive(name)
|
||||
messages.success(request, _('Archive deleted.'))
|
||||
return redirect('backups:index')
|
||||
@ -218,12 +219,12 @@ class RestoreArchiveView(BaseRestoreView):
|
||||
"""Save some data used to instantiate the form."""
|
||||
name = unquote(self.kwargs['name'])
|
||||
uuid = self.kwargs['uuid']
|
||||
repository = get_repository(uuid)
|
||||
repository = create_repository(uuid)
|
||||
return repository.get_archive_apps(name)
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Restore files from the archive on valid form submission."""
|
||||
repository = get_repository(self.kwargs['uuid'])
|
||||
repository = create_repository(self.kwargs['uuid'])
|
||||
selected_apps = form.cleaned_data['selected_apps']
|
||||
repository.restore_archive(self.kwargs['name'], selected_apps)
|
||||
return super().form_valid(form)
|
||||
@ -233,7 +234,7 @@ class DownloadArchiveView(View):
|
||||
"""View to export and download an archive as stream."""
|
||||
|
||||
def get(self, request, uuid, name):
|
||||
repository = get_repository(uuid)
|
||||
repository = create_repository(uuid)
|
||||
filename = f'{name}.tar.gz'
|
||||
|
||||
response = StreamingHttpResponse(repository.get_download_stream(name),
|
||||
@ -257,12 +258,21 @@ class AddRepositoryView(SuccessMessageMixin, FormView):
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Create and save a Borg repository."""
|
||||
path = pathlib.Path(
|
||||
form.cleaned_data.get('disk')) / 'FreedomBoxBackups'
|
||||
path = os.path.join(form.cleaned_data.get('disk'), 'FreedomBoxBackups')
|
||||
encryption = form.cleaned_data.get('encryption')
|
||||
encryption_passphrase = form.cleaned_data.get('encryption_passphrase')
|
||||
if form.cleaned_data.get('encryption') == 'none':
|
||||
if encryption == 'none':
|
||||
encryption_passphrase = None
|
||||
|
||||
credentials = {'encryption_passphrase': encryption_passphrase}
|
||||
repository = BorgRepository(path=path, credentials=credentials)
|
||||
try:
|
||||
repository.get_info()
|
||||
except BorgRepositoryDoesNotExistError:
|
||||
repository.create_repository(encryption)
|
||||
repository.save(store_credentials=True, verified=True)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class AddRemoteRepositoryView(SuccessMessageMixin, FormView):
|
||||
"""View to create a new remote backup repository."""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user