Skip to content

Commit

Permalink
Upgrade model to PrimaryModel, add GraphQL support
Browse files Browse the repository at this point in the history
  • Loading branch information
DanSheps committed Sep 12, 2024
1 parent 02b6d65 commit ca97abc
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 28 deletions.
1 change: 1 addition & 0 deletions netbox_config_backup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class NetboxConfigBackup(PluginConfig):
queues = [
'jobs'
]
graphql_schema = 'graphql.schema.schema'


config = NetboxConfigBackup
26 changes: 22 additions & 4 deletions netbox_config_backup/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@
from dcim.choices import DeviceStatusChoices
from dcim.models import Device
from ipam.models import IPAddress
from netbox.forms import NetBoxModelForm, NetBoxModelBulkEditForm
from netbox_config_backup.models import Backup
from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, CommentField

__all__ = (
'BackupForm',
'BackupFilterSetForm',
'BackupBulkEditForm',
)

from utilities.forms.rendering import FieldSet

class BackupForm(forms.ModelForm):

class BackupForm(NetBoxModelForm):
device = DynamicModelChoiceField(
label='Device',
required=False,
Expand All @@ -36,9 +40,11 @@ class BackupForm(forms.ModelForm):
'assigned_to_interface': True
},
)
comments = CommentField()

class Meta:
model = Backup
fields = ('name', 'device', 'ip', 'status')
fields = ('name', 'device', 'ip', 'status', 'description', 'comments', 'config_status')

def clean(self):
super().clean()
Expand Down Expand Up @@ -71,7 +77,7 @@ class BackupFilterSetForm(forms.Form):
'status': [DeviceStatusChoices.STATUS_ACTIVE],
'platform__napalm__ne': None,
'has_primary_ip': True,
}
},
)
ip = forms.CharField(
required=False,
Expand All @@ -84,3 +90,15 @@ class BackupFilterSetForm(forms.Form):
)


class BackupBulkEditForm(NetBoxModelBulkEditForm):

description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()

model = Backup
fieldsets = ()
nullable_fields = ()
Empty file.
14 changes: 14 additions & 0 deletions netbox_config_backup/graphql/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import strawberry_django
from netbox_config_backup import filtersets, models

from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin

__all__ = (
'BackupFilter',
)


@strawberry_django.filter(models.Backup, lookups=True)
@autotype_decorator(filtersets.BackupFilterSet)
class BackupFilter(BaseFilterMixin):
pass
17 changes: 17 additions & 0 deletions netbox_config_backup/graphql/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import List

import strawberry
import strawberry_django

from .types import *


@strawberry.type(name="Query")
class BackupQuery:
backup: BackupType = strawberry_django.field()
backup_list: List[BackupType] = strawberry_django.field()


schema = [
BackupQuery,
]
25 changes: 25 additions & 0 deletions netbox_config_backup/graphql/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Annotated

import strawberry
import strawberry_django

from netbox.graphql.types import NetBoxObjectType
from .filters import *

from netbox_config_backup import models

__all__ = (
'BackupType',
)


@strawberry_django.type(
models.Backup,
fields='__all__',
filters=BackupFilter
)
class BackupType(NetBoxObjectType):

name: str
device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
ip: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
9 changes: 7 additions & 2 deletions netbox_config_backup/models/backups.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from dcim.models import Device
from core.choices import JobStatusChoices
from netbox.models import NetBoxModel
from netbox.models import PrimaryModel

from netbox_config_backup.choices import StatusChoices
from netbox_config_backup.helpers import get_repository_dir
Expand All @@ -22,7 +22,7 @@
logger = logging.getLogger(f"netbox_config_backup")


class Backup(NetBoxModel):
class Backup(PrimaryModel):
name = models.CharField(max_length=255, unique=True)
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
status = models.CharField(
Expand All @@ -42,6 +42,10 @@ class Backup(NetBoxModel):
blank=True,
null=True
)
config_status = models.BooleanField(
blank=True,
null=True
)

objects = BackupQuerySet.as_manager()

Expand Down Expand Up @@ -95,6 +99,7 @@ def set_config(self, configs, files=('running', 'startup'), pk=None):
stored = stored_configs.get(file) if stored_configs.get(file) is not None else ''
#logger.debug(f'[{pk}] Getting new config')
current = configs.get(file) if configs.get(file) is not None else ''

#logger.debug(f'[{pk}] Starting diff for {file}')
if Differ(stored, current).is_diff():
changes = True
Expand Down
3 changes: 3 additions & 0 deletions netbox_config_backup/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class BackupTable(BaseTable):
backup_count = tables.Column(
accessor='changes'
)
config_status = tables.BooleanColumn(
verbose_name='Config Saved'
)

class Meta(BaseTable.Meta):
model = Backup
Expand Down
40 changes: 22 additions & 18 deletions netbox_config_backup/tasks.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
import logging
import sys
import traceback
from datetime import timedelta

from django.utils import timezone
from django_rq import job
from netmiko import NetmikoAuthenticationException, NetmikoTimeoutException

from core.choices import JobStatusChoices
from netbox import settings
from netbox.api.exceptions import ServiceUnavailable
from netbox.config import get_config
from netbox_config_backup.models import Backup, BackupJob, BackupCommit
from netbox_config_backup.models import BackupJob
from netbox_config_backup.utils.configs import check_config_save_status
from netbox_config_backup.utils.logger import get_logger
from netbox_config_backup.utils.rq import can_backup


def get_logger():
# Setup logging to Stdout
formatter = logging.Formatter(f'[%(asctime)s][%(levelname)s] - %(message)s')
stdouthandler = logging.StreamHandler(sys.stdout)
stdouthandler.setLevel(logging.DEBUG)
stdouthandler.setFormatter(formatter)
logger = logging.getLogger(f"netbox_config_backup")
logger.addHandler(stdouthandler)

return logger


def napalm_init(device, ip=None, extra_args={}):
from netbox import settings
username = settings.PLUGINS_CONFIG.get('netbox_napalm_plugin', {}).get('NAPALM_USERNAME', None)
Expand Down Expand Up @@ -99,7 +85,25 @@ def backup_config(backup, pk=None):
logger.info(f'{backup}: Backup started')
#logger.debug(f'[{pk}] Connecting')
d = napalm_init(backup.device, ip)
#logger.debug(f'[{pk}] Finished Connection')
#logger.debug(f'[{pk}

try:
status = check_config_save_status(d)
if status is not None:
if status and not backup.config_status:
backup.config_status = status
backup.save()
elif not status and backup.config_status:
backup.config_status = status
backup.save()
elif not status and backup.config_status is None:
backup.config_status = status
backup.save()
elif status and backup.config_status is None:
backup.config_status = status
backup.save()
except Exception as e:
logger.error(f'{backup}: had error setting backup status: {e}')

#logger.debug(f'[{pk}] Getting config')
configs = d.get_config()
Expand Down
14 changes: 12 additions & 2 deletions netbox_config_backup/templates/netbox_config_backup/backup.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,27 @@ <h5 class="card-header">
<td>{{ object.ip|placeholder }}</td>
{% endif %}
</tr>
<tr>
<th scope="row">Description</th>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>
</div>
</div>
{% include 'inc/panels/comments.html' %}
</div>

<div class="col col-md-6">
<div class="card">
<h5 class="card-header">
Status
</h5>
<div class="card-body">
<table class="table table-hover">
<tr>
<th scope="row">Config Saved</th>
<td>{{ object.config_status | placeholder }}</td>
</tr>
<tr>
<th scope="row">Scheduled</th>
<td>{{ status.scheduled | placeholder }}{% if status.scheduled %} ({{status.next_attempt}}){% endif %}</td>
Expand All @@ -66,8 +78,6 @@ <h5 class="card-header">
</table>
</div>
</div>
</div>
<div class="col col-md-6">
</div>
</div>
{% endblock %}
6 changes: 6 additions & 0 deletions netbox_config_backup/urls.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
from django.urls import path

from netbox.views.generic import ObjectChangeLogView
from . import views
from .models import Backup

urlpatterns = [
path('unassigned/', views.UnassignedBackupListView.as_view(), name='unassignedbackup_list'),
path('devices/', views.BackupListView.as_view(), name='backup_list'),
path('devices/add/', views.BackupEditView.as_view(), name='backup_add'),
path('devices/<int:pk>/', views.BackupView.as_view(), name='backup'),
path('devices/<int:pk>/changelog', ObjectChangeLogView.as_view(), name="backup_changelog", kwargs={'model': Backup}),
path('devices/<int:pk>/edit/', views.BackupEditView.as_view(), name='backup_edit'),
path('devices/<int:pk>/delete/', views.BackupDeleteView.as_view(), name='backup_delete'),
path('devices/<int:pk>/backups/', views.BackupBackupsView.as_view(), name='backup_backups'),
path('devices/<int:backup>/config/', views.ConfigView.as_view(), name='backup_config'),
path('devices/<int:backup>/config/<int:current>/', views.ConfigView.as_view(), name='backup_config'),
path('devices/<int:backup>/diff/', views.DiffView.as_view(), name='backup_diff'),
path('devices/<int:backup>/diff/<int:current>/', views.DiffView.as_view(), name='backup_diff'),
path('devices/edit/', views.BackupBulkEditView.as_view(), name='backup_bulk_edit'),
path('devices/delete/', views.BackupBulkDeleteView.as_view(), name='backup_bulk_delete'),
path('devices/<int:backup>/diff/<int:current>/<int:previous>/', views.DiffView.as_view(), name='backup_diff'),
]
20 changes: 18 additions & 2 deletions netbox_config_backup/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
from django.views import View

from core.choices import JobStatusChoices
from netbox.views.generic import ObjectDeleteView, ObjectEditView, ObjectView, ObjectListView, ObjectChildrenView
from netbox.views.generic import ObjectDeleteView, ObjectEditView, ObjectView, ObjectListView, ObjectChildrenView, \
BulkEditView, BulkDeleteView
from netbox_config_backup.filtersets import BackupFilterSet, BackupsFilterSet

from netbox_config_backup.forms import BackupForm, BackupFilterSetForm
from netbox_config_backup.forms import BackupForm, BackupFilterSetForm, BackupBulkEditForm
from netbox_config_backup.git import GitBackup
from netbox_config_backup.models import Backup, BackupJob, BackupCommitTreeChange, BackupCommit, BackupObject
from netbox_config_backup.tables import BackupTable, BackupsTable
Expand Down Expand Up @@ -141,6 +142,21 @@ def get_return_url(self, request, obj=None):
return reverse('home')


@register_model_view(Backup, 'bulk_edit')
class BackupBulkEditView(BulkEditView):
queryset = Backup.objects.all()
form = BackupBulkEditForm
filterset = BackupFilterSet
table = BackupTable


@register_model_view(Backup, 'bulk_delete')
class BackupBulkDeleteView(BulkDeleteView):
queryset = Backup.objects.all()
filterset = BackupFilterSet
table = BackupTable


@register_model_view(Backup, 'config')
class ConfigView(ObjectView):
queryset = Backup.objects.all()
Expand Down

0 comments on commit ca97abc

Please sign in to comment.