Backup download and delete.

This commit is contained in:
Eduardo Silva 2024-04-01 12:36:50 -03:00
parent 60e1d557aa
commit 31b1c663f2
10 changed files with 110 additions and 11 deletions

View file

@ -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}')

View file

@ -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),
),
]

View file

@ -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)

View file

@ -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

View file

@ -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),
),
]

View file

@ -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',

View file

@ -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'),
]

View file

@ -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:

View file

@ -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:

View file

@ -83,12 +83,19 @@
{% endif %}
</span>
</li>
<li class="list-group-item">
<b>Delete backup</b>
<span class="float-right">
<a href='javascript:void(0)' class='text-danger' data-command='delete' onclick='openCommandDialog(this)'>
<i class="far fa-trash-alt"></i>
</a>
</span>
</li>
</ul>
</div>
</div>
</div>
@ -165,5 +172,16 @@
{% block custom_page_scripts %}
<script>
function openCommandDialog(element) {
var command = element.getAttribute('data-command');
var confirmation = prompt("Please type 'delete{{ backup.id }}' to proceed.");
if (confirmation) {
var url = "/backup/delete/?uuid={{ backup.uuid }}&action=delete&confirmation=" + encodeURIComponent(confirmation);
window.location.href = url;
}
}
</script>
{% endblock %}