From 31b1c663f295b1e64bf3cba8ee94b3d31cc629c3 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Mon, 1 Apr 2024 12:36:50 -0300 Subject: [PATCH] Backup download and delete. --- backup/views.py | 38 ++++++++++++++++++- .../0006_routerbackup_backup_text_filename.py | 18 +++++++++ backup_data/models.py | 3 +- requirements.txt | 1 + .../0010_alter_router_router_type.py | 18 +++++++++ routerfleet/settings.py | 1 + routerfleet/urls.py | 4 +- routerlib/backup_functions.py | 8 ++-- routerlib/functions.py | 4 ++ templates/backup/backup_details.html | 26 +++++++++++-- 10 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 backup_data/migrations/0006_routerbackup_backup_text_filename.py create mode 100644 router_manager/migrations/0010_alter_router_router_type.py diff --git a/backup/views.py b/backup/views.py index 837e14e..f53d9f0 100644 --- a/backup/views.py +++ b/backup/views.py @@ -1,6 +1,6 @@ from django.contrib.auth.decorators import login_required -from django.http import JsonResponse -from django.shortcuts import render, get_object_or_404, redirect +from django.http import JsonResponse, HttpResponse, FileResponse +from django.shortcuts import render, get_object_or_404, redirect, Http404 from django.contrib import messages from routerlib.backup_functions import perform_backup @@ -10,6 +10,7 @@ from router_manager.models import Router from backup_data.models import RouterBackup import difflib import unicodedata +from routerlib.functions import gen_backup_name, get_router_backup_file_extension @login_required() @@ -137,3 +138,36 @@ def view_debug_run_backups(request): perform_backup(backup) return JsonResponse(data) + + +@login_required() +def view_backup_download(request): + backup = get_object_or_404(RouterBackup, uuid=request.GET.get('uuid')) + if request.GET.get('type') == 'text': + response = HttpResponse(backup.backup_text, content_type='text/plain') + if backup.backup_text_filename: + filename = backup.backup_text_filename + else: + filename = gen_backup_name(backup) + filename += f'.missing_backup_name.{get_router_backup_file_extension(backup.router.router_type)["text"]}' + print(filename) + response['Content-Disposition'] = f'attachment; filename={filename}' + return response + elif request.GET.get('type') == 'binary': + response = FileResponse(backup.backup_binary, as_attachment=True) + return response + else: + raise Http404 + + +@login_required() +def view_backup_delete(request): + backup = get_object_or_404(RouterBackup, uuid=request.GET.get('uuid')) + redirect_url = f'/router/details/?uuid={backup.router.uuid}' + if request.GET.get('confirmation') == f'delete{backup.id}': + backup.delete() + messages.success(request, 'Backup deleted successfully') + return redirect(redirect_url) + else: + messages.warning(request, 'Backup not deleted|Invalid confirmation') + return redirect(f'/backup/backup_details/?uuid={backup.uuid}') diff --git a/backup_data/migrations/0006_routerbackup_backup_text_filename.py b/backup_data/migrations/0006_routerbackup_backup_text_filename.py new file mode 100644 index 0000000..3205c62 --- /dev/null +++ b/backup_data/migrations/0006_routerbackup_backup_text_filename.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-04-01 12:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('backup_data', '0005_alter_routerbackup_backup_text_hash'), + ] + + operations = [ + migrations.AddField( + model_name='routerbackup', + name='backup_text_filename', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/backup_data/models.py b/backup_data/models.py index d0f86d9..fd10029 100644 --- a/backup_data/models.py +++ b/backup_data/models.py @@ -2,6 +2,7 @@ from django.db import models from router_manager.models import Router import uuid import hashlib +import os class RouterBackup(models.Model): @@ -19,6 +20,7 @@ class RouterBackup(models.Model): finish_time = models.DateTimeField(blank=True, null=True) backup_text = models.TextField(blank=True, null=True) backup_text_hash = models.CharField(max_length=64, blank=True, db_index=True) + backup_text_filename = models.CharField(max_length=255, blank=True, null=True) backup_binary = models.FileField(upload_to='backups/', blank=True, null=True) updated = models.DateTimeField(auto_now=True) @@ -32,4 +34,3 @@ class RouterBackup(models.Model): self.backup_text_hash = '' super(RouterBackup, self).save(*args, **kwargs) - diff --git a/requirements.txt b/requirements.txt index 3315214..d9715e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ crispy-bootstrap4==2024.1 crispy-bootstrap5==2024.2 cryptography==42.0.5 Django==5.0.3 +django-cleanup==8.1.0 django-crispy-forms==2.1 idna==3.6 paramiko==3.4.0 diff --git a/router_manager/migrations/0010_alter_router_router_type.py b/router_manager/migrations/0010_alter_router_router_type.py new file mode 100644 index 0000000..96f763d --- /dev/null +++ b/router_manager/migrations/0010_alter_router_router_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-04-01 12:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('router_manager', '0009_router_backup_profile'), + ] + + operations = [ + migrations.AlterField( + model_name='router', + name='router_type', + field=models.CharField(choices=[('monitoring', 'Monitoring Only'), ('routeros', 'Mikrotik (RouterOS)'), ('openwrt', 'OpenWRT')], max_length=100), + ), + ] diff --git a/routerfleet/settings.py b/routerfleet/settings.py index 66d8095..af27b22 100644 --- a/routerfleet/settings.py +++ b/routerfleet/settings.py @@ -37,6 +37,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django_cleanup.apps.CleanupConfig', 'crispy_forms', 'crispy_bootstrap4', 'user_manager', diff --git a/routerfleet/urls.py b/routerfleet/urls.py index 8939da1..39b98d4 100644 --- a/routerfleet/urls.py +++ b/routerfleet/urls.py @@ -4,7 +4,7 @@ from dashboard.views import view_dashboard, view_status from user_manager.views import view_manage_user, view_user_list from accounts.views import view_login, view_logout, view_create_first_user from router_manager.views import view_router_list, view_manage_router, view_router_group_list, view_ssh_key_list, view_manage_router_group, view_manage_sshkey, view_router_details -from backup.views import view_backup_profile_list, view_manage_backup_profile, view_backup_list, view_backup_details, view_debug_run_backups, view_compare_backups +from backup.views import view_backup_profile_list, view_manage_backup_profile, view_backup_list, view_backup_details, view_debug_run_backups, view_compare_backups, view_backup_download, view_backup_delete from monitoring.views import view_export_router_list, view_update_router_status @@ -30,6 +30,8 @@ urlpatterns = [ path('backup/backup_list/', view_backup_list, name='backup_list'), path('backup/backup_details/', view_backup_details, name='backup_info'), path('backup/compare/', view_compare_backups, name='compare_backups'), + path('backup/download/', view_backup_download, name='download_backup'), + path('backup/delete/', view_backup_delete, name='delete_backup'), path('monitoring/export_router_list/', view_export_router_list, name='export_router_list'), path('monitoring/update_router_status/', view_update_router_status, name='update_router_status'), ] diff --git a/routerlib/backup_functions.py b/routerlib/backup_functions.py index cdb6671..9ce4dd6 100644 --- a/routerlib/backup_functions.py +++ b/routerlib/backup_functions.py @@ -5,6 +5,7 @@ import paramiko import os from scp import SCPClient from django.core.files.base import ContentFile +from routerlib.functions import gen_backup_name def perform_backup(router_backup: RouterBackup): @@ -70,7 +71,7 @@ def execute_backup(router_backup: RouterBackup): router_backup.router.address, username=router_backup.router.username, password=router_backup.router.password, look_for_keys=False, allow_agent=False, timeout=10 ) - backup_name = f"backup-routerfleet-{router_backup.schedule_type}-{router_backup.uuid}" + backup_name = gen_backup_name(router_backup) ssh_client.exec_command(f'/system backup save name={backup_name}.backup') ssh_client.exec_command(f'/export file={backup_name}.rsc') return True, [f"{backup_name}.backup", f"{backup_name}.rsc"], error_message @@ -86,7 +87,7 @@ def execute_backup(router_backup: RouterBackup): def retrieve_backup(router_backup: RouterBackup): error_message = "" - backup_name = f"backup-routerfleet-{router_backup.schedule_type}-{router_backup.uuid}" + backup_name = gen_backup_name(router_backup) success = False ssh_client = paramiko.SSHClient() @@ -110,6 +111,7 @@ def retrieve_backup(router_backup: RouterBackup): rsc_content_cleaned = '\n'.join( line for line in rsc_content.split('\n') if not line.strip().startswith('#')) router_backup.backup_text = rsc_content_cleaned + router_backup.backup_text_filename = f"{backup_name}.rsc" with open(backup_file_path, 'rb') as backup_file: router_backup.backup_binary.save(f"{backup_name}.backup", ContentFile(backup_file.read())) @@ -141,7 +143,7 @@ def clean_up_backup_files(router_backup: RouterBackup): router_backup.router.address, username=router_backup.router.username, password=router_backup.router.password, look_for_keys=False, timeout=10, allow_agent=False ) - ssh_client.exec_command('file remove [find where name~"backup-routerfleet-"]') + ssh_client.exec_command('file remove [find where name~"routerfleet-backup-"]') else: print(f"Router type not supported: {router_backup.router.get_router_type_display()}") except Exception as e: diff --git a/routerlib/functions.py b/routerlib/functions.py index 9c9cee9..6c28c48 100644 --- a/routerlib/functions.py +++ b/routerlib/functions.py @@ -16,6 +16,10 @@ def get_router_backup_file_extension(router_type): return {'text': 'txt', 'binary': 'bin'} +def gen_backup_name(router_backup): + return f'routerfleet-backup-{router_backup.id}-{router_backup.schedule_type}-{router_backup.router.address}-{router_backup.created.strftime("%Y-%m-%d_%H-%M")}' + + def test_authentication(router_type, address, username, password, sshkey=None): router_features = get_router_features(router_type) if 'ssh' in router_features: diff --git a/templates/backup/backup_details.html b/templates/backup/backup_details.html index 173f418..c745a8a 100644 --- a/templates/backup/backup_details.html +++ b/templates/backup/backup_details.html @@ -83,12 +83,19 @@ {% endif %} - + +
  • + Delete backup + + + + + + + +
  • - - - @@ -165,5 +172,16 @@ {% block custom_page_scripts %} + + {% endblock %} \ No newline at end of file