diff --git a/netbox_dns_bridge/filtersets.py b/netbox_dns_bridge/filtersets.py index b611931..87de3a6 100644 --- a/netbox_dns_bridge/filtersets.py +++ b/netbox_dns_bridge/filtersets.py @@ -5,6 +5,8 @@ import netbox_dns.models from netbox.filtersets import NetBoxModelFilterSet from netbox_dns.choices import ZoneStatusChoices +from .models import CatalogZone + class ZoneFilterSet(NetBoxModelFilterSet): view = django_filters.ModelMultipleChoiceFilter( @@ -27,3 +29,19 @@ class ZoneFilterSet(NetBoxModelFilterSet): Q(name__icontains=value) | Q(catz_identifier__name__icontains=value) ) + + +class CatalogZoneFilterSet(NetBoxModelFilterSet): + view = django_filters.ModelMultipleChoiceFilter( + field_name="view", + queryset=netbox_dns.models.View.objects.all(), + ) + + class Meta: + model = CatalogZone + fields = ("id", "view") + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter(Q(view__name__icontains=value)) diff --git a/netbox_dns_bridge/forms.py b/netbox_dns_bridge/forms.py index 9987d40..dd7d784 100644 --- a/netbox_dns_bridge/forms.py +++ b/netbox_dns_bridge/forms.py @@ -2,12 +2,40 @@ from django import forms from django.utils.translation import gettext_lazy as _ import netbox_dns.models -from netbox.forms import NetBoxModelFilterSetForm +from netbox.forms import NetBoxModelFilterSetForm, NetBoxModelForm from netbox_dns.choices import ZoneStatusChoices from utilities.forms.fields import DynamicModelMultipleChoiceField +from .models import CatalogZone -class CatalogZoneFilterForm(NetBoxModelFilterSetForm): + +class CatalogZoneForm(NetBoxModelForm): + soa_serial = forms.IntegerField(label=_("SOA Serial")) + soa_refresh = forms.IntegerField(label=_("Refresh")) + soa_retry = forms.IntegerField(label=_("Retry")) + soa_expire = forms.IntegerField(label=_("Expire")) + soa_minimum = forms.IntegerField(label=_("Minimum")) + + view = forms.ModelChoiceField( + queryset=netbox_dns.models.View.objects.all(), + disabled=True, + required=False, + label=_("View"), + ) + + class Meta: + model = CatalogZone + fields = ( + "view", + "soa_serial", + "soa_refresh", + "soa_retry", + "soa_expire", + "soa_minimum", + ) + + +class CatalogZoneMemberFilterForm(NetBoxModelFilterSetForm): model = netbox_dns.models.Zone q = forms.CharField(required=False, label=_("Search")) @@ -19,3 +47,13 @@ class CatalogZoneFilterForm(NetBoxModelFilterSetForm): choices=ZoneStatusChoices, required=False, ) + + +class CatalogZoneFilterForm(NetBoxModelFilterSetForm): + model = CatalogZone + + q = forms.CharField(required=False, label=_("Search")) + view = DynamicModelMultipleChoiceField( + queryset=netbox_dns.models.View.objects.all(), + required=False, + ) diff --git a/netbox_dns_bridge/jobs/notify.py b/netbox_dns_bridge/jobs/notify.py index b919244..128805b 100644 --- a/netbox_dns_bridge/jobs/notify.py +++ b/netbox_dns_bridge/jobs/notify.py @@ -51,6 +51,10 @@ def _send_notify( notify_over_tcp, port, log_prefix="NOTIFY", + soa_refresh=60, + soa_retry=10, + soa_expire=1209600, + soa_minimum=0, ): """Build a DNS NOTIFY message for *zone_name* and send it to every target IP.""" fqdn = dns.name.from_text(zone_name, dns.name.root).to_text() @@ -61,7 +65,7 @@ def _send_notify( soa_rdata = dns.rdata.from_text( dns.rdataclass.IN, dns.rdatatype.SOA, - f"invalid. invalid. {soa_serial} 60 10 1209600 0", + f"invalid. invalid. {soa_serial} {soa_refresh} {soa_retry} {soa_expire} {soa_minimum}", ) msg.answer.append(dns.rrset.from_rdata(fqdn, 0, soa_rdata)) msg.use_tsig(keyring={tsig_key.name: tsig_key}, keyname=tsig_key.name) @@ -95,6 +99,10 @@ class SendClientNotify(JobRunner): view_name = kwargs["view_name"] zone_name = kwargs["zone_name"] soa_serial = kwargs["soa_serial"] + soa_refresh = kwargs.get("soa_refresh", 60) + soa_retry = kwargs.get("soa_retry", 10) + soa_expire = kwargs.get("soa_expire", 1209600) + soa_minimum = kwargs.get("soa_minimum", 0) try: view = View.objects.get(name=view_name) @@ -111,6 +119,8 @@ class SendClientNotify(JobRunner): _send_notify( zone_name, soa_serial, clients, tsig_key, view_name, notify_over_tcp, port, log_prefix="NOTIFY", + soa_refresh=soa_refresh, soa_retry=soa_retry, + soa_expire=soa_expire, soa_minimum=soa_minimum, ) except View.DoesNotExist as e: @@ -278,17 +288,31 @@ def schedule_catalog_zone_notify(view: View): return try: - serial = view.catalog_zone.soa_serial + catalog_zone = view.catalog_zone except CatalogZone.DoesNotExist: return + serial = catalog_zone.soa_serial + soa_refresh = catalog_zone.soa_refresh + soa_retry = catalog_zone.soa_retry + soa_expire = catalog_zone.soa_expire + soa_minimum = catalog_zone.soa_minimum + SendClientNotify.enqueue( zone_name="catz", soa_serial=serial, view_name=view.name, + soa_refresh=soa_refresh, + soa_retry=soa_retry, + soa_expire=soa_expire, + soa_minimum=soa_minimum, ) SendClientNotify.enqueue( zone_name=f"{view.name}.catz", soa_serial=serial, view_name=view.name, + soa_refresh=soa_refresh, + soa_retry=soa_retry, + soa_expire=soa_expire, + soa_minimum=soa_minimum, ) diff --git a/netbox_dns_bridge/management/commands/dns_transfer_endpoint/catalog_zone_manager.py b/netbox_dns_bridge/management/commands/dns_transfer_endpoint/catalog_zone_manager.py index e0dbe78..919a753 100644 --- a/netbox_dns_bridge/management/commands/dns_transfer_endpoint/catalog_zone_manager.py +++ b/netbox_dns_bridge/management/commands/dns_transfer_endpoint/catalog_zone_manager.py @@ -174,7 +174,9 @@ def _create_soa_rdataset(view: NBView) -> dns.rdataset: rdata = dns.rdata.from_text( rclass, rtype, - f"invalid. invalid. {serial} 60 10 1209600 0", + f"invalid. invalid. {serial} {serial_obj.soa_refresh} " + f"{serial_obj.soa_retry} {serial_obj.soa_expire} " + f"{serial_obj.soa_minimum}", ) # Create Rdataset and add the RDATA to it diff --git a/netbox_dns_bridge/migrations/0003_catalogzone_and_per_zone_identifier.py b/netbox_dns_bridge/migrations/0003_catalogzone_and_per_zone_identifier.py index 15aff98..a8d8ae3 100644 --- a/netbox_dns_bridge/migrations/0003_catalogzone_and_per_zone_identifier.py +++ b/netbox_dns_bridge/migrations/0003_catalogzone_and_per_zone_identifier.py @@ -88,6 +88,10 @@ class Migration(migrations.Migration): ), ), ("soa_serial", models.IntegerField(default=1)), + ("soa_refresh", models.IntegerField(default=60)), + ("soa_retry", models.IntegerField(default=10)), + ("soa_expire", models.IntegerField(default=1209600)), + ("soa_minimum", models.IntegerField(default=0)), ( "view", models.OneToOneField( diff --git a/netbox_dns_bridge/models.py b/netbox_dns_bridge/models.py index 056d3b2..5eb479e 100644 --- a/netbox_dns_bridge/models.py +++ b/netbox_dns_bridge/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.urls import reverse # import netbox.models import netbox_dns.models @@ -13,6 +14,10 @@ class CatalogZone(models.Model): related_name="catalog_zone", ) soa_serial = models.IntegerField(default=1) + soa_refresh = models.IntegerField(default=60) + soa_retry = models.IntegerField(default=10) + soa_expire = models.IntegerField(default=1209600) + soa_minimum = models.IntegerField(default=0) class Meta: ordering = ("view__name",) @@ -20,6 +25,9 @@ class CatalogZone(models.Model): def __str__(self): return f"{self.view.name}: {self.soa_serial}" + def get_absolute_url(self): + return reverse("plugins:netbox_dns_bridge:catalogzone_edit", args=[self.pk]) + class CatalogZoneMemberIdentifier(models.Model): _netbox_private = True diff --git a/netbox_dns_bridge/navigation.py b/netbox_dns_bridge/navigation.py index 79a8ae4..f8b616c 100644 --- a/netbox_dns_bridge/navigation.py +++ b/netbox_dns_bridge/navigation.py @@ -1,13 +1,20 @@ from django.utils.translation import gettext_lazy as _ from netbox.plugins import PluginMenu, PluginMenuItem -information_menu_item = PluginMenuItem( - link="plugins:netbox_dns_bridge:catz", +catalog_zones_menu_item = PluginMenuItem( + link="plugins:netbox_dns_bridge:catalog_zones", link_text=_("Catalog Zones"), permissions=[], staff_only=True, ) +catalog_zone_members_menu_item = PluginMenuItem( + link="plugins:netbox_dns_bridge:catalog_zone_members", + link_text=_("Catalog Zone Members"), + permissions=[], + staff_only=True, +) + notify_menu_item = PluginMenuItem( link="plugins:netbox_dns_bridge:notify", link_text=_("Notify"), @@ -18,7 +25,10 @@ notify_menu_item = PluginMenuItem( menu = PluginMenu( label=_("DNS Bridge"), groups=( - (_("Information"), (information_menu_item, notify_menu_item)), + ( + _("Information"), + (catalog_zones_menu_item, catalog_zone_members_menu_item, notify_menu_item), + ), ), icon_class="mdi mdi-bridge", ) diff --git a/netbox_dns_bridge/tables.py b/netbox_dns_bridge/tables.py index b2ac39a..61bbb8d 100644 --- a/netbox_dns_bridge/tables.py +++ b/netbox_dns_bridge/tables.py @@ -4,8 +4,10 @@ from django.utils.translation import gettext_lazy as _ import netbox_dns.models from netbox.tables import NetBoxTable +from .models import CatalogZone -class CatalogZoneTable(NetBoxTable): + +class CatalogZoneMemberTable(NetBoxTable): name = tables.Column(linkify=True) view = tables.Column( accessor="view__name", @@ -22,3 +24,35 @@ class CatalogZoneTable(NetBoxTable): model = netbox_dns.models.Zone fields = ("name", "view", "status", "catz_identifier") default_columns = ("name", "view", "status", "catz_identifier") + + +class CatalogZoneTable(NetBoxTable): + view = tables.Column( + accessor="view__name", + verbose_name=_("View"), + linkify=True, + ) + soa_serial = tables.Column(verbose_name=_("SOA Serial")) + soa_refresh = tables.Column(verbose_name=_("Refresh")) + soa_retry = tables.Column(verbose_name=_("Retry")) + soa_expire = tables.Column(verbose_name=_("Expire")) + soa_minimum = tables.Column(verbose_name=_("Minimum")) + + class Meta(NetBoxTable.Meta): + model = CatalogZone + fields = ( + "view", + "soa_serial", + "soa_refresh", + "soa_retry", + "soa_expire", + "soa_minimum", + ) + default_columns = ( + "view", + "soa_serial", + "soa_refresh", + "soa_retry", + "soa_expire", + "soa_minimum", + ) diff --git a/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_edit.html b/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_edit.html new file mode 100644 index 0000000..f95313d --- /dev/null +++ b/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_edit.html @@ -0,0 +1,4 @@ +{% extends 'generic/object_edit.html' %} +{% load i18n %} + +{% block title %}{% trans "Edit Catalog Zone" %}{% endblock %} diff --git a/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_list.html b/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_list.html new file mode 100644 index 0000000..e8abf68 --- /dev/null +++ b/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_list.html @@ -0,0 +1,4 @@ +{% extends 'generic/object_list.html' %} +{% load i18n %} + +{% block title %}{% trans "Catalog Zones" %}{% endblock %} diff --git a/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_members.html b/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_members.html new file mode 100644 index 0000000..1352a41 --- /dev/null +++ b/netbox_dns_bridge/templates/netbox_dns_bridge/catalog_zone_members.html @@ -0,0 +1,4 @@ +{% extends 'generic/object_list.html' %} +{% load i18n %} + +{% block title %}{% trans "Catalog Zone Members" %}{% endblock %} diff --git a/netbox_dns_bridge/templates/netbox_dns_bridge/catz_overview.html b/netbox_dns_bridge/templates/netbox_dns_bridge/catz_overview.html deleted file mode 100644 index 581bbe1..0000000 --- a/netbox_dns_bridge/templates/netbox_dns_bridge/catz_overview.html +++ /dev/null @@ -1,37 +0,0 @@ -{% extends 'generic/object_list.html' %} -{% load i18n %} - -{% block title %}{% trans "Catalog Zone Info" %}{% endblock %} - -{% block content %} -
| {% trans "View" %} | -{% trans "SOA Serial" %} | -
|---|---|
| {{ cz.view.name }} | -{{ cz.soa_serial }} | -