diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml new file mode 100644 index 0000000..98c3db8 --- /dev/null +++ b/.github/workflows/ci-tests.yml @@ -0,0 +1,59 @@ +name: CI +on: [push, pull_request] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + env: + NETBOX_CONFIGURATION: netbox.configuration_testing + strategy: + matrix: + python-version: ['3.8', '3.9', '3.10', '3.11'] + services: + redis: + image: redis + ports: + - 6379:6379 + postgres: + image: postgres + env: + POSTGRES_USER: netbox + POSTGRES_PASSWORD: netbox + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Check out NetBox + uses: actions/checkout@v4 + with: + repository: netbox-community/netbox + ref: master + path: netbox + + - name: Check out repo + uses: actions/checkout@v4 + with: + path: netbox-config-backup + + - name: Install dependencies & set up configuration + run: | + python -m pip install --upgrade pip + pip install -r netbox\requirements.txt + pip install pycodestyle coverage tblib + pip install -e netbox-config-backup + + - name: Run tests + run: coverage run --source="netbox-config-backup/" netbox/manage.py test netbox-config-backup/ --parallel \ No newline at end of file diff --git a/netbox_config_backup/models/backups.py b/netbox_config_backup/models/backups.py index 4f2ee6f..dd7828d 100644 --- a/netbox_config_backup/models/backups.py +++ b/netbox_config_backup/models/backups.py @@ -49,8 +49,23 @@ class Meta: ordering = ['name'] @property - def backup_count(self): - return self.changes.count() + def last_backup(self): + return self.jobs.filter(status=JobResultStatusChoices.STATUS_COMPLETED).last().completed + + @property + def next_attempt(self): + return self.jobs.filter( + status__in=[ + JobResultStatusChoices.STATUS_SCHEDULED, + JobResultStatusChoices.STATUS_PENDING, + JobResultStatusChoices.STATUS_RUNNING + ] + ).last().scheduled + + @property + def last_change(self): + + return self.changes.last().commit.time def get_absolute_url(self): return reverse('plugins:netbox_config_backup:backup', args=[self.pk]) diff --git a/netbox_config_backup/querysets/__init__.py b/netbox_config_backup/querysets/__init__.py index 5fe8a17..8f53592 100644 --- a/netbox_config_backup/querysets/__init__.py +++ b/netbox_config_backup/querysets/__init__.py @@ -1,4 +1,5 @@ from django.db import models +from django.db.models import Count from extras.choices import JobResultStatusChoices from utilities.querysets import RestrictedQuerySet @@ -9,14 +10,15 @@ 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 + return self.prefetch_related('jobs', 'changes', 'changes__commit') + #.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')).order_by('-id').values('commit__time')[:1] + #) + #) \ No newline at end of file diff --git a/netbox_config_backup/tables.py b/netbox_config_backup/tables.py index 0876bc3..1d49725 100644 --- a/netbox_config_backup/tables.py +++ b/netbox_config_backup/tables.py @@ -43,16 +43,23 @@ class BackupTable(BaseTable): ) last_backup = tables.DateTimeColumn() next_attempt = tables.DateTimeColumn() + last_change = tables.DateTimeColumn() + backup_count = tables.Column( + accessor='changes' + ) class Meta(BaseTable.Meta): model = Backup fields = ( - 'pk', 'name', 'device', 'last_backup', 'next_attempt', 'backup_count' + 'pk', 'name', 'device', 'last_backup', 'next_attempt', 'last_change', 'backup_count' ) default_columns = ( 'pk', 'name', 'device', 'last_backup', 'next_attempt', 'backup_count' ) + def render_backup_count(self, value): + return f'{value.count()}' + class BackupsTable(NetBoxTable): date = tables.Column( diff --git a/netbox_config_backup/tests/__init__.py b/netbox_config_backup/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/netbox_config_backup/tests/test_models.py b/netbox_config_backup/tests/test_models.py new file mode 100644 index 0000000..c609c55 --- /dev/null +++ b/netbox_config_backup/tests/test_models.py @@ -0,0 +1,30 @@ +from django.core.exceptions import ValidationError +from django.test import TestCase + +from dcim.models import Site, Manufacturer, DeviceType, DeviceRole, Device, Interface +from ipam.models import IPAddress +from netbox_config_backup.models import * + +class TestBackup(TestCase): + + @classmethod + def setUpTestData(cls): + + site = Site.objects.create(name='Site 1') + manufacturer = Manufacturer.objects.create(name='Manufacturer 1') + device_type = DeviceType.objects.create(model='Device Type 1', manufacturer=manufacturer) + role = DeviceRole.objects.create(name='Switch') + device = Device.objects.create( + name='Device 1', + site=site, + device_type=device_type, + role=role, + status='active' + ) + interface = Interface.objects.create(name='Interface 1', device=device, type='1000baset') + address = IPAddress.objects.create(assigned_object=interface, address='10.0.0.1/32') + device.primary_ip4 = address + device.save() + + def test_create_backup(self): + pass