From cc54ba0f73023a46924a478b4fb1b375aff84735 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Sun, 17 Mar 2024 17:24:56 -0300 Subject: [PATCH] Manage backup profiles --- backup/forms.py | 131 ++++++++++++++++++ .../0002_alter_backupprofile_weekly_day.py | 18 +++ ...0003_alter_backupprofile_retry_interval.py | 18 +++ backup/models.py | 4 +- backup/views.py | 49 ++++++- router_manager/forms.py | 3 +- router_manager/models.py | 2 - routerfleet/urls.py | 5 + templates/backup/backup_profile_form.html | 65 +++++++++ templates/backup/backup_profile_list.html | 75 ++++++++++ templates/base.html | 12 +- templates/router_manager/router_list.html | 28 ++-- 12 files changed, 392 insertions(+), 18 deletions(-) create mode 100644 backup/forms.py create mode 100644 backup/migrations/0002_alter_backupprofile_weekly_day.py create mode 100644 backup/migrations/0003_alter_backupprofile_retry_interval.py create mode 100644 templates/backup/backup_profile_form.html create mode 100644 templates/backup/backup_profile_list.html diff --git a/backup/forms.py b/backup/forms.py new file mode 100644 index 0000000..4049ed0 --- /dev/null +++ b/backup/forms.py @@ -0,0 +1,131 @@ +from django import forms +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Submit, Row, Column, HTML, Field, Div +from .models import BackupProfile + + +class BackupProfileForm(forms.ModelForm): + class Meta: + model = BackupProfile + fields = [ + 'name', 'daily_backup', 'weekly_backup', 'monthly_backup', + 'daily_retenion', 'weekly_retention', 'monthly_retenion', + 'retain_backups_on_error', 'daily_day_monday', 'daily_day_tuesday', + 'daily_day_wednesday', 'daily_day_thursday', 'daily_day_friday', + 'daily_day_saturday', 'daily_day_sunday', 'weekly_day', + 'monthly_day', 'daily_hour', 'weekly_hour', 'monthly_hour', + 'max_retry', 'retry_interval', 'backup_interval' + ] + # widgets = { + # 'weekly_day': forms.Select(), + # 'monthly_day': forms.Select(), + # 'daily_hour': forms.Select(choices=HOUR_CHOICES), + # 'weekly_hour': forms.Select(choices=HOUR_CHOICES), + # 'monthly_hour': forms.Select(choices=HOUR_CHOICES), + # 'max_retry': forms.Select(), + # 'retry_interval': forms.Select(), + # 'backup_interval': forms.Select(), + # } + + def __init__(self, *args, **kwargs): + super(BackupProfileForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = 'post' + if self.instance.pk: + delete_html = "Delete" + else: + delete_html = '' + + self.fields['daily_day_monday'].label = 'Monday' + self.fields['daily_day_tuesday'].label = 'Tuesday' + self.fields['daily_day_wednesday'].label = 'Wednesday' + self.fields['daily_day_thursday'].label = 'Thursday' + self.fields['daily_day_friday'].label = 'Friday' + self.fields['daily_day_saturday'].label = 'Saturday' + self.fields['daily_day_sunday'].label = 'Sunday' + self.fields['daily_backup'].label = 'Daily' + self.fields['weekly_backup'].label = 'Weekly' + self.fields['monthly_backup'].label = 'Monthly' + self.fields['daily_retenion'].label = 'Retention (days)' + self.fields['weekly_retention'].label = 'Retention (days)' + self.fields['monthly_retenion'].label = 'Retention (days)' + + self.helper.layout = Layout( + Div(Div('name', css_class='col-md-12'), css_class='row'), + Div( + Div('daily_backup', css_class='col-md-4'), + Div('weekly_backup', css_class='col-md-4'), + Div('monthly_backup', css_class='col-md-4'), + css_class='row'), + + Div( + Div(HTML('

Daily Backups

'), css_class='col-md-12'), + Div('daily_hour', css_class='col-md-4'), + Div('daily_retenion', css_class='col-md-4'), + Div(css_class='col-md-4'), + Div('daily_day_monday', css_class='col-md-4'), + Div('daily_day_tuesday', css_class='col-md-4'), + Div('daily_day_wednesday', css_class='col-md-4'), + Div('daily_day_thursday',css_class='col-md-4'), + Div('daily_day_friday', css_class='col-md-4'), + Div('daily_day_saturday', css_class='col-md-4'), + Div('daily_day_sunday', css_class='col-md-4'), + css_id='daily_settings', css_class='row' + ), + + Div( + Div(HTML('

Weekly Backups

'), css_class='col-md-12'), + Div('weekly_hour', css_class='col-md-4'), + Div('weekly_retention', css_class='col-md-4'), + Div('weekly_day', css_class='col-md-4'), + css_id='weekly_settings', css_class='row' + ), + + Div( + Div(HTML('

Monthly Backups

'), css_class='col-md-12'), + Div('monthly_hour', css_class='col-md-4'), + Div('monthly_retenion', css_class='col-md-4'), + Div('monthly_day', css_class='col-md-4'), + css_id='monthly_settings', css_class='row' + ), + + Div( + Div(HTML('

Backup Settings

'), css_class='col-md-12'), + Div('max_retry', css_class='col-md-4'), + Div('retry_interval', css_class='col-md-4'), + Div('backup_interval', css_class='col-md-4'), + Div('retain_backups_on_error', css_class='col-md-12'), + css_id='misc_settings', css_class='row' + ), + Row( + Column( + Submit('submit', 'Salvar', css_class='btn btn-success'), + HTML(' Back '), + HTML(delete_html), + css_class='col-md-12' + ) + ) + ) + + def clean(self): + cleaned_data = super().clean() + daily_backup = cleaned_data.get('daily_backup') + weekly_backup = cleaned_data.get('weekly_backup') + monthly_backup = cleaned_data.get('monthly_backup') + + daily_day_monday = cleaned_data.get('daily_day_monday') + daily_day_tuesday = cleaned_data.get('daily_day_tuesday') + daily_day_wednesday = cleaned_data.get('daily_day_wednesday') + daily_day_thursday = cleaned_data.get('daily_day_thursday') + daily_day_friday = cleaned_data.get('daily_day_friday') + daily_day_saturday = cleaned_data.get('daily_day_saturday') + daily_day_sunday = cleaned_data.get('daily_day_sunday') + + if daily_backup: + if not daily_day_monday and not daily_day_tuesday and not daily_day_wednesday and not daily_day_thursday and not daily_day_friday and not daily_day_saturday and not daily_day_sunday: + raise forms.ValidationError('You must select at least one day for daily backups') + + if not daily_backup and not weekly_backup and not monthly_backup: + raise forms.ValidationError('You must select at least one backup type') + + return cleaned_data \ No newline at end of file diff --git a/backup/migrations/0002_alter_backupprofile_weekly_day.py b/backup/migrations/0002_alter_backupprofile_weekly_day.py new file mode 100644 index 0000000..0e51011 --- /dev/null +++ b/backup/migrations/0002_alter_backupprofile_weekly_day.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-03-17 14:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('backup', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='backupprofile', + name='weekly_day', + field=models.CharField(choices=[('monday', 'Monday'), ('tuesday', 'Tuesday'), ('wednesday', 'Wednesday'), ('thursday', 'Thursday'), ('friday', 'Friday'), ('saturday', 'Saturday'), ('sunday', 'Sunday')], default='sunday', max_length=10), + ), + ] diff --git a/backup/migrations/0003_alter_backupprofile_retry_interval.py b/backup/migrations/0003_alter_backupprofile_retry_interval.py new file mode 100644 index 0000000..b1f20cc --- /dev/null +++ b/backup/migrations/0003_alter_backupprofile_retry_interval.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-03-17 19:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('backup', '0002_alter_backupprofile_weekly_day'), + ] + + operations = [ + migrations.AlterField( + model_name='backupprofile', + name='retry_interval', + field=models.IntegerField(choices=[(1, '1 Minute'), (15, '15 Minutes'), (30, '30 Minutes'), (60, '1 Hour')], default=30), + ), + ] diff --git a/backup/models.py b/backup/models.py index c813d6f..35e9b14 100644 --- a/backup/models.py +++ b/backup/models.py @@ -29,7 +29,7 @@ class BackupProfile(models.Model): daily_day_saturday = models.BooleanField(default=True) daily_day_sunday = models.BooleanField(default=True) - weekly_day = models.CharField(max_length=10, choices=(('monday', 'Monday'), ('tuesday', 'Tuesday'), ('wednesday', 'Wednesday'), ('thursday', 'Thursday'), ('friday', 'Friday'), ('saturday', 'Saturday'), ('sunday', 'Sunday'))) + weekly_day = models.CharField(max_length=10, default='sunday', choices=(('monday', 'Monday'), ('tuesday', 'Tuesday'), ('wednesday', 'Wednesday'), ('thursday', 'Thursday'), ('friday', 'Friday'), ('saturday', 'Saturday'), ('sunday', 'Sunday'))) monthly_day = models.IntegerField(default=1, choices=((1, '1st'), (7, '7th'), (14, '14th'), (21, '21st'), (28, '28th'))) daily_hour = models.IntegerField(default=3, choices=HOUR_CHOICES) @@ -37,7 +37,7 @@ class BackupProfile(models.Model): monthly_hour = models.IntegerField(default=0, choices=HOUR_CHOICES) max_retry = models.IntegerField(default=3, choices=((1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'))) - retry_interval = models.IntegerField(default=30, choices=(('1', '1 Minute'), ('15', '15 Minutes'), ('30', '30 Minutes'), ('60', '1 Hour'))) + retry_interval = models.IntegerField(default=30, choices=((1, '1 Minute'), (15, '15 Minutes'), (30, '30 Minutes'), (60, '1 Hour'))) backup_interval = models.IntegerField(default=60, choices=((0, 'No interval'), (5, '5 seconds'), (60, '1 minute'))) updated = models.DateTimeField(auto_now=True) diff --git a/backup/views.py b/backup/views.py index 91ea44a..83944de 100644 --- a/backup/views.py +++ b/backup/views.py @@ -1,3 +1,48 @@ -from django.shortcuts import render +from django.contrib.auth.decorators import login_required +from django.shortcuts import render, get_object_or_404, redirect +from django.contrib import messages +from .models import BackupProfile +from .forms import BackupProfileForm +from router_manager.models import Router -# Create your views here. + +@login_required() +def view_backup_profile_list(request): + context = { + 'backup_profile_list': BackupProfile.objects.all().order_by('name'), + 'page_title': 'Backup Profiles' + } + return render(request, 'backup/backup_profile_list.html', context) + + +@login_required() +def view_manage_backup_profile(request): + if request.GET.get('uuid'): + backup_profile = get_object_or_404(BackupProfile, uuid=request.GET.get('uuid')) + if request.GET.get('action') == 'delete': + if request.GET.get('confirmation') == 'delete': + if Router.objects.filter(backup_profile=backup_profile).exists(): + messages.warning(request, 'Backup profile in use|Backup profile is in use and cannot be deleted') + return redirect('backup_profile_list') + else: + backup_profile.delete() + messages.success(request, 'Backup profile deleted successfully') + return redirect('backup_profile_list') + else: + messages.warning(request, 'Backup profile not deleted|Invalid confirmation') + return redirect('backup_profile_list') + else: + backup_profile = None + + form = BackupProfileForm(request.POST or None, instance=backup_profile) + if form.is_valid(): + form.save() + messages.success(request, 'Backup Profile saved successfully') + return redirect('backup_profile_list') + + context = { + 'form': form, + 'page_title': 'Manage Backup Profile', + 'instance': backup_profile + } + return render(request, 'backup/backup_profile_form.html', context=context) diff --git a/router_manager/forms.py b/router_manager/forms.py index b5fbb36..7ac1c92 100644 --- a/router_manager/forms.py +++ b/router_manager/forms.py @@ -11,7 +11,7 @@ class RouterForm(forms.ModelForm): class Meta: model = Router - fields = ['name', 'address', 'username', 'password', 'ssh_key', 'monitoring', 'router_type', 'enabled'] + fields = ['name', 'address', 'username', 'password', 'ssh_key', 'monitoring', 'router_type', 'enabled', 'backup_profile'] def __init__(self, *args, **kwargs): super(RouterForm, self).__init__(*args, **kwargs) @@ -35,6 +35,7 @@ class RouterForm(forms.ModelForm): css_class='form-row' ), 'ssh_key', + 'backup_profile', 'router_type', 'monitoring', 'enabled', diff --git a/router_manager/models.py b/router_manager/models.py index 9132270..e26e2ee 100644 --- a/router_manager/models.py +++ b/router_manager/models.py @@ -73,5 +73,3 @@ class BackupSchedule(models.Model): created = models.DateTimeField(auto_now_add=True) uuid = models.UUIDField(unique=True, editable=False, default=uuid.uuid4) - - diff --git a/routerfleet/urls.py b/routerfleet/urls.py index 34b755d..56fe537 100644 --- a/routerfleet/urls.py +++ b/routerfleet/urls.py @@ -4,6 +4,8 @@ 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 +from backup.views import view_backup_profile_list, view_manage_backup_profile + urlpatterns = [ path('admin/', admin.site.urls), @@ -20,4 +22,7 @@ urlpatterns = [ 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_sshkey/', view_manage_sshkey, name='manage_sshkey'), + path('backup/profile_list/', view_backup_profile_list, name='backup_profile_list'), + path('backup/manage_profile/', view_manage_backup_profile, name='manage_backup_profile') + ] diff --git a/templates/backup/backup_profile_form.html b/templates/backup/backup_profile_form.html new file mode 100644 index 0000000..36a5383 --- /dev/null +++ b/templates/backup/backup_profile_form.html @@ -0,0 +1,65 @@ +{% extends 'base.html' %} +{% load crispy_forms_tags %} + +{% block content %} +
+
+
+ {% if page_title %} +
+

{{ page_title }}

+
+ {% endif %} +
+
+ {% csrf_token %} + {% crispy form %} +
+
+
+
+
+ +{% endblock %} + +{% block custom_page_scripts %} + + + +{% endblock %} \ No newline at end of file diff --git a/templates/backup/backup_profile_list.html b/templates/backup/backup_profile_list.html new file mode 100644 index 0000000..575e6a3 --- /dev/null +++ b/templates/backup/backup_profile_list.html @@ -0,0 +1,75 @@ +{% extends 'base.html' %} + + +{% block content %} +
+
+
+ {% if page_title %} +
+

{{ page_title }}

+
+ {% endif %} +
+
+
+ + + + + + + + + + + + + + {% for backup_profile in backup_profile_list %} + + + + + + + + + + + + + {% endfor %} + +
NameRouter CountDailyWeeklyMonthly
{{ backup_profile.name }} + {{ backup_profile.router_set.count }} + + {% if backup_profile.daily_backup %} + + {% endif %} + + {% if backup_profile.weekly_backup %} + + {% endif %} + + {% if backup_profile.monthly_backup %} + + {% endif %} + + +
+
+ +
+
+ + +
+ +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 760c9e2..c2fa579 100644 --- a/templates/base.html +++ b/templates/base.html @@ -111,7 +111,17 @@

- + + + +