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 %}
-
-
-
-
-
- {% if catalog_zones %}
-
-
-
- | {% trans "View" %} |
- {% trans "SOA Serial" %} |
-
-
-
- {% for cz in catalog_zones %}
-
- | {{ cz.view.name }} |
- {{ cz.soa_serial }} |
-
- {% endfor %}
-
-
- {% else %}
-
{% trans "No catalog zones initialized." %}
- {% endif %}
-
-
-
-
-{{ block.super }}
-{% endblock %}
diff --git a/netbox_dns_bridge/tests/test_catalog_zone_manager.py b/netbox_dns_bridge/tests/test_catalog_zone_manager.py
index 2d8b1e2..2fa92fe 100644
--- a/netbox_dns_bridge/tests/test_catalog_zone_manager.py
+++ b/netbox_dns_bridge/tests/test_catalog_zone_manager.py
@@ -174,6 +174,23 @@ class TestCreateZone:
soa = zone.get_rdataset(origin, dns.rdatatype.SOA)
assert soa[0].serial == catz_serial
+ def test_soa_parameters_from_model(self, make_zone, make_view):
+ view = make_view(name="cz-soa-params-view")
+ make_zone(name="soa-params.test", view=view)
+ CatalogZone.objects.filter(view=view).update(
+ soa_refresh=300,
+ soa_retry=100,
+ soa_expire=86400,
+ soa_minimum=3600,
+ )
+ zone = catzm.create_zone("catz", "cz-soa-params-view")
+ origin = dns.name.from_text("catz", dns.name.root)
+ soa = zone.get_rdataset(origin, dns.rdatatype.SOA)
+ assert soa[0].refresh == 300
+ assert soa[0].retry == 100
+ assert soa[0].expire == 86400
+ assert soa[0].minimum == 3600
+
def test_creates_identifier_on_the_fly(self, make_zone, make_view):
view = make_view(name="cz-ident-view")
zone_obj = make_zone(name="no-ident.test", view=view)
diff --git a/netbox_dns_bridge/tests/test_models.py b/netbox_dns_bridge/tests/test_models.py
index 02810fc..ba717c7 100644
--- a/netbox_dns_bridge/tests/test_models.py
+++ b/netbox_dns_bridge/tests/test_models.py
@@ -20,6 +20,28 @@ class TestCatalogZone:
cz = CatalogZone.objects.create(view=view)
assert cz.soa_serial == 1
+ def test_default_soa_parameters(self, make_view):
+ view = make_view(name="cz-soa-default-view")
+ cz = CatalogZone.objects.create(view=view)
+ assert cz.soa_refresh == 60
+ assert cz.soa_retry == 10
+ assert cz.soa_expire == 1209600
+ assert cz.soa_minimum == 0
+
+ def test_custom_soa_parameters(self, make_view):
+ view = make_view(name="cz-soa-custom-view")
+ cz = CatalogZone.objects.create(
+ view=view,
+ soa_refresh=300,
+ soa_retry=100,
+ soa_expire=86400,
+ soa_minimum=3600,
+ )
+ assert cz.soa_refresh == 300
+ assert cz.soa_retry == 100
+ assert cz.soa_expire == 86400
+ assert cz.soa_minimum == 3600
+
def test_one_to_one_relationship(self, make_view):
view = make_view(name="cz-oto-view")
cz = CatalogZone.objects.create(view=view, soa_serial=7)
diff --git a/netbox_dns_bridge/tests/test_notify.py b/netbox_dns_bridge/tests/test_notify.py
index e03d8d7..dc03a0e 100644
--- a/netbox_dns_bridge/tests/test_notify.py
+++ b/netbox_dns_bridge/tests/test_notify.py
@@ -229,12 +229,22 @@ class TestScheduleCatalogZoneNotify:
nh.schedule_catalog_zone_notify(view)
assert mock_enq.call_count == 2
mock_enq.assert_any_call(
- zone_name="catz", soa_serial=42, view_name="catz-notify-view"
+ zone_name="catz",
+ soa_serial=42,
+ view_name="catz-notify-view",
+ soa_refresh=60,
+ soa_retry=10,
+ soa_expire=1209600,
+ soa_minimum=0,
)
mock_enq.assert_any_call(
zone_name="catz-notify-view.catz",
soa_serial=42,
view_name="catz-notify-view",
+ soa_refresh=60,
+ soa_retry=10,
+ soa_expire=1209600,
+ soa_minimum=0,
)
def test_does_not_enqueue_when_notify_clients_disabled(self, make_view, monkeypatch):
diff --git a/netbox_dns_bridge/tests/test_views.py b/netbox_dns_bridge/tests/test_views.py
index d39dc05..137f359 100644
--- a/netbox_dns_bridge/tests/test_views.py
+++ b/netbox_dns_bridge/tests/test_views.py
@@ -1,4 +1,4 @@
-"""Tests for views: CatzOverviewView, NotifyOverviewView — auth gate and context."""
+"""Tests for views: CatalogZoneMembersView, CatalogZoneListView, CatalogZoneEditView, NotifyOverviewView — auth gate and context."""
import pytest
from django.test import Client
from django.contrib.auth import get_user_model
@@ -28,36 +28,113 @@ def regular_user(db):
@pytest.mark.django_db
-class TestCatzOverviewView:
+class TestCatalogZoneMembersView:
def test_unauthenticated_redirects_to_login(self, client):
- resp = client.get("/plugins/dns-bridge/catz/")
+ resp = client.get("/plugins/dns-bridge/catalog-zone-members/")
assert resp.status_code in (302, 403)
def test_regular_user_forbidden(self, client, regular_user):
client.login(username="regular", password="testpass")
- resp = client.get("/plugins/dns-bridge/catz/")
+ resp = client.get("/plugins/dns-bridge/catalog-zone-members/")
assert resp.status_code in (302, 403)
def test_superuser_can_access(self, client, superuser):
client.login(username="admin", password="testpass")
- resp = client.get("/plugins/dns-bridge/catz/")
+ resp = client.get("/plugins/dns-bridge/catalog-zone-members/")
assert resp.status_code == 200
- def test_context_contains_catalog_zones(self, client, superuser, catz_serial, make_zone):
+
+@pytest.mark.django_db
+class TestCatalogZoneListView:
+ def test_unauthenticated_redirects_to_login(self, client):
+ resp = client.get("/plugins/dns-bridge/catalog-zones/")
+ assert resp.status_code in (302, 403)
+
+ def test_regular_user_forbidden(self, client, regular_user):
+ client.login(username="regular", password="testpass")
+ resp = client.get("/plugins/dns-bridge/catalog-zones/")
+ assert resp.status_code in (302, 403)
+
+ def test_superuser_can_access(self, client, superuser):
+ client.login(username="admin", password="testpass")
+ resp = client.get("/plugins/dns-bridge/catalog-zones/")
+ assert resp.status_code == 200
+
+ def test_list_contains_catalog_zones(self, client, superuser, catz_serial, make_zone):
zone = make_zone(name="ctx.test")
client.login(username="admin", password="testpass")
- resp = client.get("/plugins/dns-bridge/catz/")
- assert "catalog_zones" in resp.context
- cz_list = list(resp.context["catalog_zones"])
+ resp = client.get("/plugins/dns-bridge/catalog-zones/")
+ cz_list = list(resp.context["table"].data)
assert len(cz_list) >= 1
cz = next(c for c in cz_list if c.view_id == zone.view_id)
assert cz.soa_serial == catz_serial
- def test_context_no_catalog_zones_when_empty(self, client, superuser):
+ def test_list_empty_when_no_catalog_zones(self, client, superuser):
client.login(username="admin", password="testpass")
- resp = client.get("/plugins/dns-bridge/catz/")
- assert "catalog_zones" in resp.context
- assert list(resp.context["catalog_zones"]) == []
+ resp = client.get("/plugins/dns-bridge/catalog-zones/")
+ cz_list = list(resp.context["table"].data)
+ assert cz_list == []
+
+
+@pytest.mark.django_db
+class TestCatalogZoneEditView:
+ def test_unauthenticated_redirects_to_login(self, client, make_view):
+ view = make_view(name="edit-cz-view")
+ from netbox_dns_bridge.models import CatalogZone
+ cz = CatalogZone.objects.create(view=view)
+ resp = client.get(f"/plugins/dns-bridge/catalog-zones/{cz.pk}/edit/")
+ assert resp.status_code in (302, 403)
+
+ def test_regular_user_forbidden(self, client, regular_user, make_view):
+ view = make_view(name="edit-cz-view-forbidden")
+ from netbox_dns_bridge.models import CatalogZone
+ cz = CatalogZone.objects.create(view=view)
+ client.login(username="regular", password="testpass")
+ resp = client.get(f"/plugins/dns-bridge/catalog-zones/{cz.pk}/edit/")
+ assert resp.status_code in (302, 403)
+
+ def test_superuser_can_get_edit_form(self, client, superuser, make_view):
+ view = make_view(name="edit-cz-view-get")
+ from netbox_dns_bridge.models import CatalogZone
+ cz = CatalogZone.objects.create(view=view)
+ client.login(username="admin", password="testpass")
+ resp = client.get(f"/plugins/dns-bridge/catalog-zones/{cz.pk}/edit/")
+ assert resp.status_code == 200
+
+ def test_superuser_can_update_soa_fields(self, client, superuser, make_view):
+ view = make_view(name="edit-cz-view-post")
+ from netbox_dns_bridge.models import CatalogZone
+ cz = CatalogZone.objects.create(view=view)
+ client.login(username="admin", password="testpass")
+ resp = client.post(
+ f"/plugins/dns-bridge/catalog-zones/{cz.pk}/edit/",
+ {
+ "soa_serial": 100,
+ "soa_refresh": 300,
+ "soa_retry": 100,
+ "soa_expire": 86400,
+ "soa_minimum": 3600,
+ },
+ )
+ cz.refresh_from_db()
+ assert cz.soa_serial == 100
+ assert cz.soa_refresh == 300
+ assert cz.soa_retry == 100
+ assert cz.soa_expire == 86400
+ assert cz.soa_minimum == 3600
+
+ def test_no_create_route(self, client, superuser):
+ client.login(username="admin", password="testpass")
+ resp = client.get("/plugins/dns-bridge/catalog-zones/add/")
+ assert resp.status_code == 404
+
+ def test_no_delete_route(self, client, superuser, make_view):
+ view = make_view(name="edit-cz-view-delete")
+ from netbox_dns_bridge.models import CatalogZone
+ cz = CatalogZone.objects.create(view=view)
+ client.login(username="admin", password="testpass")
+ resp = client.get(f"/plugins/dns-bridge/catalog-zones/{cz.pk}/delete/")
+ assert resp.status_code == 404
@pytest.mark.django_db
diff --git a/netbox_dns_bridge/urls.py b/netbox_dns_bridge/urls.py
index 207211c..18d97e2 100644
--- a/netbox_dns_bridge/urls.py
+++ b/netbox_dns_bridge/urls.py
@@ -1,8 +1,23 @@
from django.urls import path
-from .views import CatzOverviewView, NotifyOverviewView
+from .views import (
+ CatalogZoneEditView,
+ CatalogZoneListView,
+ CatalogZoneMembersView,
+ NotifyOverviewView,
+)
urlpatterns = [
- path("catz/", CatzOverviewView.as_view(), name="catz"),
+ path("catalog-zones/", CatalogZoneListView.as_view(), name="catalog_zones"),
+ path(
+ "catalog-zones//edit/",
+ CatalogZoneEditView.as_view(),
+ name="catalogzone_edit",
+ ),
+ path(
+ "catalog-zone-members/",
+ CatalogZoneMembersView.as_view(),
+ name="catalog_zone_members",
+ ),
path("notify/", NotifyOverviewView.as_view(), name="notify"),
]
diff --git a/netbox_dns_bridge/views.py b/netbox_dns_bridge/views.py
index c5c751d..1b310f8 100644
--- a/netbox_dns_bridge/views.py
+++ b/netbox_dns_bridge/views.py
@@ -4,34 +4,52 @@ from django.utils.decorators import method_decorator
from django.views import View
import netbox_dns.models
-from netbox.views.generic import ObjectListView
+from netbox.views.generic import ObjectEditView, ObjectListView
-from .filtersets import ZoneFilterSet
-from .forms import CatalogZoneFilterForm
+from .filtersets import CatalogZoneFilterSet, ZoneFilterSet
+from .forms import (
+ CatalogZoneFilterForm,
+ CatalogZoneForm,
+ CatalogZoneMemberFilterForm,
+)
from .models import CatalogZone, SeenTransferClients
-from .tables import CatalogZoneTable
+from .tables import CatalogZoneMemberTable, CatalogZoneTable
@method_decorator(
user_passes_test(lambda u: u.is_superuser), name="dispatch"
)
-class CatzOverviewView(ObjectListView):
+class CatalogZoneListView(ObjectListView):
+ queryset = CatalogZone.objects.select_related("view").order_by("view__name")
+ table = CatalogZoneTable
+ filterset = CatalogZoneFilterSet
+ filterset_form = CatalogZoneFilterForm
+ template_name = "netbox_dns_bridge/catalog_zone_list.html"
+ actions = ("edit", "export")
+
+
+@method_decorator(
+ user_passes_test(lambda u: u.is_superuser), name="dispatch"
+)
+class CatalogZoneEditView(ObjectEditView):
+ queryset = CatalogZone.objects.select_related("view")
+ form = CatalogZoneForm
+ template_name = "netbox_dns_bridge/catalog_zone_edit.html"
+
+
+@method_decorator(
+ user_passes_test(lambda u: u.is_superuser), name="dispatch"
+)
+class CatalogZoneMembersView(ObjectListView):
queryset = netbox_dns.models.Zone.objects.select_related(
"view", "catz_identifier"
).order_by("view__name", "name")
- table = CatalogZoneTable
+ table = CatalogZoneMemberTable
filterset = ZoneFilterSet
- filterset_form = CatalogZoneFilterForm
- template_name = "netbox_dns_bridge/catz_overview.html"
+ filterset_form = CatalogZoneMemberFilterForm
+ template_name = "netbox_dns_bridge/catalog_zone_members.html"
actions = ()
- def get_extra_context(self, request):
- context = super().get_extra_context(request)
- context["catalog_zones"] = CatalogZone.objects.select_related(
- "view"
- ).order_by("view__name")
- return context
-
@method_decorator(
user_passes_test(lambda u: u.is_superuser), name="dispatch"