mirror of
https://github.com/eduardogsilva/routerfleet.git
synced 2025-07-28 06:44:32 +02:00
Perform backup tasks
This commit is contained in:
parent
a12a126d38
commit
aaed53ec5f
8 changed files with 189 additions and 10 deletions
|
@ -1,11 +1,14 @@
|
||||||
from django.shortcuts import render
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from django.db.models import Q
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from backup.models import BackupProfile
|
from backup.models import BackupProfile
|
||||||
from backup_data.models import RouterBackup
|
from backup_data.models import RouterBackup
|
||||||
from router_manager.models import Router, BackupSchedule
|
from router_manager.models import Router, BackupSchedule
|
||||||
|
from routerlib.backup_functions import perform_backup
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
|
|
||||||
def next_weekday(now, weekday, hour):
|
def next_weekday(now, weekday, hour):
|
||||||
|
@ -69,7 +72,7 @@ def calculate_next_backup(backup_profile):
|
||||||
return next_daily_backup, next_weekly_backup, next_monthly_backup
|
return next_daily_backup, next_weekly_backup, next_monthly_backup
|
||||||
|
|
||||||
|
|
||||||
def generate_backup_schedule(request):
|
def view_generate_backup_schedule(request):
|
||||||
data = {
|
data = {
|
||||||
'backup_schedule_created': 0,
|
'backup_schedule_created': 0,
|
||||||
'daily_backup_schedule_created': 0,
|
'daily_backup_schedule_created': 0,
|
||||||
|
@ -138,3 +141,97 @@ def generate_backup_schedule(request):
|
||||||
schedule.save()
|
schedule.save()
|
||||||
data['monthly_backup_schedule_removed'] += 1
|
data['monthly_backup_schedule_removed'] += 1
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
|
def create_backup_tasks_from_schedule_list(schedule_list, schedule_type):
|
||||||
|
tasks_created = 0
|
||||||
|
for schedule in schedule_list:
|
||||||
|
if schedule_type == 'daily':
|
||||||
|
schedule_time = schedule.next_daily_backup
|
||||||
|
schedule.next_daily_backup = None
|
||||||
|
elif schedule_type == 'weekly':
|
||||||
|
schedule_time = schedule.next_weekly_backup
|
||||||
|
schedule.next_weekly_backup = None
|
||||||
|
elif schedule_type == 'monthly':
|
||||||
|
schedule_time = schedule.next_monthly_backup
|
||||||
|
schedule.next_monthly_backup = None
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
schedule.save()
|
||||||
|
|
||||||
|
backup = RouterBackup.objects.create(
|
||||||
|
router=schedule.router, schedule_time=schedule_time, schedule_type=schedule_type
|
||||||
|
)
|
||||||
|
tasks_created += 1
|
||||||
|
backup.save()
|
||||||
|
backup.router.routerstatus.backup_lock = backup.schedule_time
|
||||||
|
backup.router.routerstatus.save()
|
||||||
|
|
||||||
|
return tasks_created
|
||||||
|
|
||||||
|
|
||||||
|
def view_create_backup_tasks(request):
|
||||||
|
data = {
|
||||||
|
'daily_backup_tasks_created': 0,
|
||||||
|
'weekly_backup_tasks_created': 0,
|
||||||
|
'monthly_backup_tasks_created': 0
|
||||||
|
}
|
||||||
|
# Priorize monthly, then weekly, then daily.
|
||||||
|
monthly_pending_schedule_list = BackupSchedule.objects.filter(
|
||||||
|
next_monthly_backup__lte=timezone.now(), router__enabled=True, router__routerstatus__backup_lock__isnull=True
|
||||||
|
).filter(
|
||||||
|
Q(router__monitoring=False) | Q(router__monitoring=True, router__routerstatus__status_online=True)
|
||||||
|
)
|
||||||
|
data['monthly_backup_tasks_created'] = create_backup_tasks_from_schedule_list(
|
||||||
|
monthly_pending_schedule_list, 'monthly'
|
||||||
|
)
|
||||||
|
|
||||||
|
weekly_pending_schedule_list = BackupSchedule.objects.filter(
|
||||||
|
next_weekly_backup__lte=timezone.now(), router__enabled=True, router__routerstatus__backup_lock__isnull=True
|
||||||
|
).filter(
|
||||||
|
Q(router__monitoring=False) | Q(router__monitoring=True, router__routerstatus__status_online=True)
|
||||||
|
)
|
||||||
|
data['weekly_backup_tasks_created'] = create_backup_tasks_from_schedule_list(
|
||||||
|
weekly_pending_schedule_list, 'weekly'
|
||||||
|
)
|
||||||
|
|
||||||
|
daily_pending_schedule_list = BackupSchedule.objects.filter(
|
||||||
|
next_daily_backup__lte=timezone.now(), router__enabled=True, router__routerstatus__backup_lock__isnull=True
|
||||||
|
).filter(
|
||||||
|
Q(router__monitoring=False) | Q(router__monitoring=True, router__routerstatus__status_online=True)
|
||||||
|
)
|
||||||
|
data['daily_backup_tasks_created'] = create_backup_tasks_from_schedule_list(
|
||||||
|
daily_pending_schedule_list, 'daily'
|
||||||
|
)
|
||||||
|
|
||||||
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
|
def view_perform_backup_tasks(request):
|
||||||
|
data = {
|
||||||
|
'backup_tasks_performed': 0
|
||||||
|
}
|
||||||
|
max_execution_time = 45 # seconds
|
||||||
|
execution_start_time = timezone.now()
|
||||||
|
pending_backup_list = RouterBackup.objects.filter(success=False, error=False).filter(
|
||||||
|
Q(schedule_time__lte=timezone.now(), next_retry__isnull=True) | Q(next_retry__lte=timezone.now())
|
||||||
|
).filter(
|
||||||
|
Q(router__monitoring=False) | Q(router__monitoring=True, router__routerstatus__status_online=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
for backup in pending_backup_list:
|
||||||
|
perform_backup(backup)
|
||||||
|
data['backup_tasks_performed'] += 1
|
||||||
|
if backup.router.backup_profile.backup_interval >= 60:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if timezone.now() - execution_start_time > timedelta(seconds=max_execution_time):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if backup.router.backup_profile.backup_interval > 0:
|
||||||
|
time.sleep(backup.router.backup_profile.backup_interval)
|
||||||
|
if timezone.now() - execution_start_time > timedelta(seconds=max_execution_time):
|
||||||
|
break
|
||||||
|
|
||||||
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
18
router_manager/migrations/0011_routerstatus_backup_lock.py
Normal file
18
router_manager/migrations/0011_routerstatus_backup_lock.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-04-03 11:57
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('router_manager', '0010_alter_router_router_type'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='routerstatus',
|
||||||
|
name='backup_lock',
|
||||||
|
field=models.DateTimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
18
router_manager/migrations/0012_alter_router_router_type.py
Normal file
18
router_manager/migrations/0012_alter_router_router_type.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-04-03 14:03
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('router_manager', '0011_routerstatus_backup_lock'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='router',
|
||||||
|
name='router_type',
|
||||||
|
field=models.CharField(choices=[('monitoring', 'Monitoring Only'), ('routeros', 'Mikrotik (RouterOS)')], max_length=100),
|
||||||
|
),
|
||||||
|
]
|
|
@ -26,7 +26,7 @@ class Router(models.Model):
|
||||||
monitoring = models.BooleanField(default=True)
|
monitoring = models.BooleanField(default=True)
|
||||||
backup_profile = models.ForeignKey(BackupProfile, on_delete=models.SET_NULL, null=True, blank=True)
|
backup_profile = models.ForeignKey(BackupProfile, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
|
|
||||||
router_type = models.CharField(max_length=100, choices=(('monitoring', 'Monitoring Only'), ('routeros', 'Mikrotik (RouterOS)'), ('openwrt', 'OpenWRT')))
|
router_type = models.CharField(max_length=100, choices=(('monitoring', 'Monitoring Only'), ('routeros', 'Mikrotik (RouterOS)')))
|
||||||
enabled = models.BooleanField(default=True)
|
enabled = models.BooleanField(default=True)
|
||||||
|
|
||||||
updated = models.DateTimeField(auto_now=True)
|
updated = models.DateTimeField(auto_now=True)
|
||||||
|
@ -43,6 +43,7 @@ class RouterStatus(models.Model):
|
||||||
last_status_change = models.DateTimeField(blank=True, null=True)
|
last_status_change = models.DateTimeField(blank=True, null=True)
|
||||||
last_backup = models.DateTimeField(blank=True, null=True)
|
last_backup = models.DateTimeField(blank=True, null=True)
|
||||||
last_backup_failed = models.DateTimeField(blank=True, null=True)
|
last_backup_failed = models.DateTimeField(blank=True, null=True)
|
||||||
|
backup_lock = models.DateTimeField(blank=True, null=True)
|
||||||
|
|
||||||
updated = models.DateTimeField(auto_now=True)
|
updated = models.DateTimeField(auto_now=True)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.utils import timezone
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
from backup_data.models import RouterBackup
|
||||||
from .models import Router, RouterGroup, RouterStatus, SSHKey, BackupSchedule
|
from .models import Router, RouterGroup, RouterStatus, SSHKey, BackupSchedule
|
||||||
from .forms import RouterForm, RouterGroupForm, SSHKeyForm
|
from .forms import RouterForm, RouterGroupForm, SSHKeyForm
|
||||||
|
|
||||||
|
@ -145,3 +148,25 @@ def view_manage_sshkey(request):
|
||||||
'instance': sshkey
|
'instance': sshkey
|
||||||
}
|
}
|
||||||
return render(request, 'generic_form.html', context=context)
|
return render(request, 'generic_form.html', context=context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def view_create_instant_backup_task(request):
|
||||||
|
router = get_object_or_404(Router, uuid=request.GET.get('uuid'))
|
||||||
|
router_details_url = f'/router/details/?uuid={router.uuid}'
|
||||||
|
if RouterBackup.objects.filter(router=router, success=False, error=False).exists():
|
||||||
|
messages.warning(request, 'Backup task not created|Active router backup task already exists')
|
||||||
|
return redirect(router_details_url)
|
||||||
|
if router.routerstatus.backup_lock is not None:
|
||||||
|
messages.warning(request, 'Backup task not created|Router backup is currently locked')
|
||||||
|
return redirect(router_details_url)
|
||||||
|
if not router.backup_profile:
|
||||||
|
messages.warning(request, 'Backup task not created|Router has no backup profile')
|
||||||
|
return redirect(router_details_url)
|
||||||
|
|
||||||
|
router_backup = RouterBackup.objects.create(router=router, schedule_time=timezone.now(), schedule_type='instant')
|
||||||
|
router.routerstatus.backup_lock = router_backup.schedule_time
|
||||||
|
router.routerstatus.save()
|
||||||
|
messages.success(request, 'Backup task created successfully')
|
||||||
|
return redirect(router_details_url)
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,10 @@ from django.urls import path
|
||||||
from dashboard.views import view_dashboard, view_status
|
from dashboard.views import view_dashboard, view_status
|
||||||
from user_manager.views import view_manage_user, view_user_list
|
from user_manager.views import view_manage_user, view_user_list
|
||||||
from accounts.views import view_login, view_logout, view_create_first_user
|
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 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, view_create_instant_backup_task
|
||||||
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 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
|
from monitoring.views import view_export_router_list, view_update_router_status
|
||||||
from backup_data.views import generate_backup_schedule
|
from backup_data.views import view_generate_backup_schedule, view_create_backup_tasks, view_perform_backup_tasks
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -26,6 +26,7 @@ urlpatterns = [
|
||||||
path('router/ssh_keys/', view_ssh_key_list, name='ssh_keys_list'),
|
path('router/ssh_keys/', view_ssh_key_list, name='ssh_keys_list'),
|
||||||
path('router/manage_group/', view_manage_router_group, name='manage_router_group'),
|
path('router/manage_group/', view_manage_router_group, name='manage_router_group'),
|
||||||
path('router/manage_sshkey/', view_manage_sshkey, name='manage_sshkey'),
|
path('router/manage_sshkey/', view_manage_sshkey, name='manage_sshkey'),
|
||||||
|
path('router/create_instant_backup/', view_create_instant_backup_task, name='create_instant_backup_task'),
|
||||||
path('backup/profile_list/', view_backup_profile_list, name='backup_profile_list'),
|
path('backup/profile_list/', view_backup_profile_list, name='backup_profile_list'),
|
||||||
path('backup/manage_profile/', view_manage_backup_profile, name='manage_backup_profile'),
|
path('backup/manage_profile/', view_manage_backup_profile, name='manage_backup_profile'),
|
||||||
path('backup/backup_list/', view_backup_list, name='backup_list'),
|
path('backup/backup_list/', view_backup_list, name='backup_list'),
|
||||||
|
@ -35,5 +36,7 @@ urlpatterns = [
|
||||||
path('backup/delete/', view_backup_delete, name='delete_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/export_router_list/', view_export_router_list, name='export_router_list'),
|
||||||
path('monitoring/update_router_status/', view_update_router_status, name='update_router_status'),
|
path('monitoring/update_router_status/', view_update_router_status, name='update_router_status'),
|
||||||
path('cron/generate_backup_schedule/', generate_backup_schedule, name='generate_backup_schedule'),
|
path('cron/generate_backup_schedule/', view_generate_backup_schedule, name='generate_backup_schedule'),
|
||||||
|
path('cron/create_backup_tasks/', view_create_backup_tasks, name='create_backup_tasks'),
|
||||||
|
path('cron/perform_backup_tasks/', view_perform_backup_tasks, name='perform_backup_tasks'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,15 +10,21 @@ from routerlib.functions import gen_backup_name
|
||||||
|
|
||||||
def perform_backup(router_backup: RouterBackup):
|
def perform_backup(router_backup: RouterBackup):
|
||||||
if router_backup.success or router_backup.error:
|
if router_backup.success or router_backup.error:
|
||||||
|
router_backup.router.routerstatus.backup_lock = None
|
||||||
|
router_backup.router.routerstatus.save()
|
||||||
return
|
return
|
||||||
if not router_backup.router.backup_profile:
|
if not router_backup.router.backup_profile:
|
||||||
router_backup.error = True
|
router_backup.error = True
|
||||||
router_backup.error_message = "No backup profile assigned"
|
router_backup.error_message = "No backup profile assigned"
|
||||||
router_backup.save()
|
router_backup.save()
|
||||||
|
router_backup.router.routerstatus.backup_lock = None
|
||||||
|
router_backup.router.routerstatus.save()
|
||||||
return
|
return
|
||||||
if router_backup.retry_count > router_backup.router.backup_profile.max_retry:
|
if router_backup.retry_count > router_backup.router.backup_profile.max_retry:
|
||||||
router_backup.error = True
|
router_backup.error = True
|
||||||
router_backup.save()
|
router_backup.save()
|
||||||
|
router_backup.router.routerstatus.backup_lock = None
|
||||||
|
router_backup.router.routerstatus.save()
|
||||||
return
|
return
|
||||||
|
|
||||||
if router_backup.backup_pending_retrieval:
|
if router_backup.backup_pending_retrieval:
|
||||||
|
@ -39,6 +45,8 @@ def perform_backup(router_backup: RouterBackup):
|
||||||
router_backup.error_message = ''
|
router_backup.error_message = ''
|
||||||
router_backup.success = True
|
router_backup.success = True
|
||||||
router_backup.save()
|
router_backup.save()
|
||||||
|
router_backup.router.routerstatus.backup_lock = None
|
||||||
|
router_backup.router.routerstatus.save()
|
||||||
else:
|
else:
|
||||||
handle_backup_failure(router_backup, error_message)
|
handle_backup_failure(router_backup, error_message)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -85,6 +85,15 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="list-group-item">
|
||||||
|
<b>Instant Backup</b>
|
||||||
|
<span class="float-right">
|
||||||
|
<a href="/router/create_instant_backup/?uuid={{ router.uuid }}">
|
||||||
|
Backup now
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<b>Notes</b>
|
<b>Notes</b>
|
||||||
<span class="float-right">{{ router.internal_notes|default_if_none:""|linebreaksbr }}</span>
|
<span class="float-right">{{ router.internal_notes|default_if_none:""|linebreaksbr }}</span>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue