diff --git a/netbox_config_backup/models/__init__.py b/netbox_config_backup/models/__init__.py index 9572001..584e229 100644 --- a/netbox_config_backup/models/__init__.py +++ b/netbox_config_backup/models/__init__.py @@ -1,4 +1,5 @@ -from netbox_config_backup.models.backups import Backup, BackupCommit, BackupFile, BackupObject, BackupCommitTreeChange +from netbox_config_backup.models.backups import Backup +from netbox_config_backup.models.repository import BackupCommit, BackupFile, BackupObject, BackupCommitTreeChange from netbox_config_backup.models.jobs import BackupJob diff --git a/netbox_config_backup/models/backups.py b/netbox_config_backup/models/backups.py index 7b9f2e3..02cdefb 100644 --- a/netbox_config_backup/models/backups.py +++ b/netbox_config_backup/models/backups.py @@ -12,12 +12,11 @@ from extras.choices import JobResultStatusChoices from netbox.models import NetBoxModel -from netbox_config_backup.choices import FileTypeChoices, CommitTreeChangeTypeChoices, StatusChoices +from netbox_config_backup.choices import StatusChoices from netbox_config_backup.helpers import get_repository_dir -from utilities.querysets import RestrictedQuerySet -from .abstract import BigIDModel from netbox_config_backup.utils.rq import remove_queued +from ..querysets import BackupQuerySet from ..utils import Differ logger = logging.getLogger(f"netbox_config_backup") @@ -44,31 +43,11 @@ class Backup(NetBoxModel): null=True ) - objects = RestrictedQuerySet.as_manager() + objects = BackupQuerySet.as_manager() class Meta: ordering = ['name'] - @property - def last_backup(self): - job = self.jobs.filter(status=JobResultStatusChoices.STATUS_COMPLETED).order_by('completed').last() - if job is not None: - return job.completed - return None - - @property - def next_attempt(self): - job = self.jobs.filter(status__in=['pending', 'running']).order_by('scheduled').last() - if job is not None: - return job.scheduled - return None - - @property - def last_change(self): - if self.changes.count() == 0: - return None - return self.changes.last().commit.time - @property def backup_count(self): return self.changes.count() @@ -89,12 +68,6 @@ def enqueue_if_needed(self): from netbox_config_backup.utils.rq import enqueue_if_needed return enqueue_if_needed(self) - def clean(self): - if not self.device and self.ip: - self.ip = None - - super().clean() - def requeue(self): self.jobs.filter( ~Q(status=JobResultStatusChoices.STATUS_COMPLETED) & @@ -195,61 +168,4 @@ def set_config(self, configs, files=('running', 'startup'), pk=None): @classmethod def get_repository_dir(cls): - return get_repository_dir() - - -class BackupCommit(BigIDModel): - sha = models.CharField(max_length=64) - time = models.DateTimeField() - - def __str__(self): - return self.sha - - -class BackupObject(BigIDModel): - sha = models.CharField(max_length=64, unique=True) - - def __str__(self): - return f'{self.sha}' - - -class BackupFile(BigIDModel): - backup = models.ForeignKey(to=Backup, on_delete=models.CASCADE, null=False, blank=False, related_name='files') - type = models.CharField(max_length=10, choices=FileTypeChoices, null=False, blank=False) - - class Meta: - unique_together = ['backup', 'type'] - - def __str__(self): - return f'{self.name}.{self.type}' - - @property - def name(self): - return f'{self.backup.uuid}' - - @property - def path(self): - return f'{self.name}.{self.type}' - - -class BackupCommitTreeChange(BigIDModel): - backup = models.ForeignKey(to=Backup, on_delete=models.CASCADE, null=False, blank=False, related_name='changes') - file = models.ForeignKey(to=BackupFile, on_delete=models.CASCADE, null=False, blank=False, related_name='changes') - - commit = models.ForeignKey(to=BackupCommit, on_delete=models.PROTECT, related_name='changes') - type = models.CharField(max_length=10) - old = models.ForeignKey(to=BackupObject, on_delete=models.PROTECT, related_name='previous', null=True) - new = models.ForeignKey(to=BackupObject, on_delete=models.PROTECT, related_name='changes', null=True) - - def __str__(self): - return f'{self.commit.sha}-{self.type}' - - def filename(self): - return f'{self.backup.uuid}.{self.type}' - - def get_absolute_url(self): - return reverse('plugins:netbox_config_backup:backup_config', kwargs={'backup': self.backup.pk, 'current': self.pk}) - - @property - def previous(self): - return self.backup.changes.filter(file__type=self.file.type, commit__time__lt=self.commit.time).last() \ No newline at end of file + return get_repository_dir() \ No newline at end of file diff --git a/netbox_config_backup/models/repository.py b/netbox_config_backup/models/repository.py new file mode 100644 index 0000000..5e27f97 --- /dev/null +++ b/netbox_config_backup/models/repository.py @@ -0,0 +1,63 @@ +from django.db import models +from django.urls import reverse + +from netbox_config_backup.choices import FileTypeChoices +from netbox_config_backup.models import Backup +from netbox_config_backup.models.abstract import BigIDModel + + +class BackupCommit(BigIDModel): + sha = models.CharField(max_length=64) + time = models.DateTimeField() + + def __str__(self): + return self.sha + + +class BackupObject(BigIDModel): + sha = models.CharField(max_length=64, unique=True) + + def __str__(self): + return f'{self.sha}' + + +class BackupFile(BigIDModel): + backup = models.ForeignKey(to=Backup, on_delete=models.CASCADE, null=False, blank=False, related_name='files') + type = models.CharField(max_length=10, choices=FileTypeChoices, null=False, blank=False) + + class Meta: + unique_together = ['backup', 'type'] + + def __str__(self): + return f'{self.name}.{self.type}' + + @property + def name(self): + return f'{self.backup.uuid}' + + @property + def path(self): + return f'{self.name}.{self.type}' + + +class BackupCommitTreeChange(BigIDModel): + backup = models.ForeignKey(to=Backup, on_delete=models.CASCADE, null=False, blank=False, related_name='changes') + file = models.ForeignKey(to=BackupFile, on_delete=models.CASCADE, null=False, blank=False, related_name='changes') + + commit = models.ForeignKey(to=BackupCommit, on_delete=models.PROTECT, related_name='changes') + type = models.CharField(max_length=10) + old = models.ForeignKey(to=BackupObject, on_delete=models.PROTECT, related_name='previous', null=True) + new = models.ForeignKey(to=BackupObject, on_delete=models.PROTECT, related_name='changes', null=True) + + def __str__(self): + return f'{self.commit.sha}-{self.type}' + + def filename(self): + return f'{self.backup.uuid}.{self.type}' + + def get_absolute_url(self): + return reverse('plugins:netbox_config_backup:backup_config', kwargs={'backup': self.backup.pk, 'current': self.pk}) + + @property + def previous(self): + return self.backup.changes.filter(file__type=self.file.type, commit__time__lt=self.commit.time).last() \ No newline at end of file diff --git a/netbox_config_backup/querysets/__init__.py b/netbox_config_backup/querysets/__init__.py new file mode 100644 index 0000000..5fe8a17 --- /dev/null +++ b/netbox_config_backup/querysets/__init__.py @@ -0,0 +1,22 @@ +from django.db import models + +from extras.choices import JobResultStatusChoices +from utilities.querysets import RestrictedQuerySet + + + +class BackupQuerySet(RestrictedQuerySet): + def default_annotate(self): + from netbox_config_backup.models import BackupJob, BackupCommitTreeChange + + return self.prefetch_related('jobs').annotate( + last_backup=models.Subquery( + BackupJob.objects.filter(backup=models.OuterRef('id'), status=JobResultStatusChoices.STATUS_COMPLETED).order_by('completed').values('completed')[:1] + ), + next_attempt=models.Subquery( + BackupJob.objects.filter(backup=models.OuterRef('id'), status__in=['pending', 'running']).order_by('scheduled').values('scheduled')[:1] + ), + last_change=models.Subquery( + BackupCommitTreeChange.objects.filter(backup=models.OuterRef('id')).values('commit__time')[:1] + ) + ) \ No newline at end of file diff --git a/netbox_config_backup/views.py b/netbox_config_backup/views.py index d55e950..5d22dd7 100644 --- a/netbox_config_backup/views.py +++ b/netbox_config_backup/views.py @@ -21,17 +21,7 @@ class BackupListView(ObjectListView): - queryset = Backup.objects.filter(device__isnull=False).prefetch_related('jobs').annotate( - last_backup=models.Subquery( - BackupJob.objects.filter(backup=models.OuterRef('id'), status=JobResultStatusChoices.STATUS_COMPLETED).order_by('completed').values('completed')[:1] - ), - next_attempt=models.Subquery( - BackupJob.objects.filter(backup=models.OuterRef('id'), status__in=['pending', 'running']).order_by('scheduled').values('scheduled')[:1] - ), - last_change=models.Subquery( - BackupCommitTreeChange.objects.filter(backup=models.OuterRef('id')).values('commit__time')[:1] - ) - ) + queryset = Backup.objects.filter(device__isnull=False).default_annotate() filterset = BackupFilterSet filterset_form = BackupFilterSetForm @@ -40,17 +30,8 @@ class BackupListView(ObjectListView): class UnassignedBackupListView(ObjectListView): - queryset = Backup.objects.filter(device__isnull=True).annotate( - last_backup=models.Subquery( - BackupJob.objects.filter(backup=models.OuterRef('id'), status=JobResultStatusChoices.STATUS_COMPLETED).order_by('completed').values('completed')[:1] - ), - next_attempt=models.Subquery( - BackupJob.objects.filter(backup=models.OuterRef('id'), status__in=['pending', 'running']).order_by('scheduled').values('scheduled')[:1] - ), - last_change=models.Subquery( - BackupCommitTreeChange.objects.filter(backup=models.OuterRef('id')).values('commit__time')[:1] - ) - ) + queryset = Backup.objects.filter(device__isnull=True).default_annotate() + filterset = BackupFilterSet filterset_form = BackupFilterSetForm table = BackupTable @@ -59,7 +40,7 @@ class UnassignedBackupListView(ObjectListView): @register_model_view(Backup) class BackupView(ObjectView): - queryset = Backup.objects.all() + queryset = Backup.objects.all().default_annotate() template_name = 'netbox_config_backup/backup.html' def get_extra_context(self, request, instance): @@ -94,7 +75,8 @@ def get_extra_context(self, request, instance): @register_model_view(Backup, name='backups') class BackupBackupsView(ObjectChildrenView): - queryset = Backup.objects.all() + queryset = Backup.objects.all().default_annotate() + template_name = 'netbox_config_backup/backups.html' child_model = BackupCommitTreeChange table = BackupsTable diff --git a/setup.py b/setup.py index 01135f3..758207b 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='netbox_config_backup', - version='1.5.2', + version='1.5.3', description='NetBox Configuration Backup', long_description='Plugin to backup device configuration', url='https://github.com/dansheps/netbox-config-backup/',