Add configurable catalog zone naming
- New setting 'catalog_zone_name' (default: 'catz') to change the name of the per-view catalog zone. Full DNS names (multiple labels) are supported; names are handled as dns.name.Name objects, making the comparison case-insensitive and tolerant of trailing dots. - New setting 'catalog_zone_name_includes_view' (default: False). When enabled, the catalog zone name is prefixed with the view name: '<view>.<catalog_zone_name>'. - The transfer endpoint now serves exactly the one configured catalog zone name per view instead of accepting both 'catz' and '<view>.catz'. - The catalog zone NOTIFY now targets only the configured name instead of notifying for both variants. Scheduling moved to catalog_zone_manager, which owns all catalog zone naming.
This commit is contained in:
1 parent
5c673d9903
commit
fe24dbdd98
5 files changed
+89
-48
No files matched your search
@@ -1,7 +1,7 @@
|
||||
from netbox.plugins import PluginConfig
|
||||
from django.conf import settings
|
||||
|
||||
__version__ = "1.6.0-rc1"
|
||||
__version__ = "1.6.0-rc2"
|
||||
|
||||
|
||||
class DNSBridgeConfig(PluginConfig):
|
||||
|
||||
@@ -12,7 +12,7 @@ from django.db import close_old_connections, OperationalError
|
||||
from netbox.jobs import JobRunner
|
||||
from netbox_dns.models import View, Zone, Record
|
||||
from netbox_dns_bridge.utils import get_logger
|
||||
from netbox_dns_bridge.models import SeenTransferClient, CatalogZone
|
||||
from netbox_dns_bridge.models import SeenTransferClient
|
||||
|
||||
LOGGER = get_logger(__name__)
|
||||
SETTINGS = settings.PLUGINS_CONFIG.get("netbox_dns_bridge", {})
|
||||
@@ -281,38 +281,3 @@ def schedule_ns_notify(zone: Zone):
|
||||
view_name=zone.view.name,
|
||||
soa_serial=soa_serial,
|
||||
)
|
||||
|
||||
|
||||
def schedule_catalog_zone_notify(view: View):
|
||||
if not SETTINGS.get("notify_clients", False):
|
||||
return
|
||||
|
||||
try:
|
||||
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,
|
||||
)
|
||||
+57
-5
@@ -8,11 +8,37 @@ from netbox_dns_bridge.models import CatalogZone, CatalogZoneMember
|
||||
from netbox_dns_bridge.jobs import notify
|
||||
from uuid import uuid4
|
||||
from base64 import b32encode
|
||||
from django.conf import settings
|
||||
from django.db import transaction, close_old_connections, OperationalError
|
||||
|
||||
|
||||
LOGGER = get_logger(__name__)
|
||||
|
||||
SETTINGS = settings.PLUGINS_CONFIG.get("netbox_dns_bridge", {})
|
||||
|
||||
|
||||
def catalog_zone_name(view_name: str) -> dns.name.Name:
|
||||
"""Return the fully qualified catalog zone name for a view as a
|
||||
dns.name.Name.
|
||||
|
||||
The name is configured via the ``catalog_zone_name`` setting (fallback:
|
||||
``catz``). With ``catalog_zone_name_includes_view`` enabled, the view
|
||||
name is prepended: ``<view>.<catalog_zone_name>``.
|
||||
"""
|
||||
base = dns.name.from_text(SETTINGS.get("catalog_zone_name") or "catz")
|
||||
if SETTINGS.get("catalog_zone_name_includes_view", False):
|
||||
return dns.name.from_text(view_name, origin=base)
|
||||
return base
|
||||
|
||||
|
||||
def is_catalog_zone_query(qname: dns.name.Name, view_name: str) -> bool:
|
||||
"""Check whether a queried name is the catalog zone name of a view.
|
||||
|
||||
dns.name.Name equality is case-insensitive, so queries from BIND match
|
||||
regardless of case.
|
||||
"""
|
||||
return qname == catalog_zone_name(view_name)
|
||||
|
||||
|
||||
def increment_soa_serial(view: NBView) -> int:
|
||||
with transaction.atomic():
|
||||
@@ -47,13 +73,13 @@ def increment_soa_serial(view: NBView) -> int:
|
||||
close_old_connections()
|
||||
return None
|
||||
|
||||
notify.schedule_catalog_zone_notify(view)
|
||||
schedule_catalog_zone_notify(view)
|
||||
return serial
|
||||
|
||||
|
||||
def create_zone(name, view_name) -> dns.zone.Zone:
|
||||
# Zone origin
|
||||
origin = dns.name.from_text(name, dns.name.root)
|
||||
def create_zone(view_name) -> dns.zone.Zone:
|
||||
# Zone origin: the configured catalog zone name for this view
|
||||
origin = catalog_zone_name(view_name)
|
||||
|
||||
# Create a new empty zone
|
||||
zone = dns.zone.Zone(origin)
|
||||
@@ -117,7 +143,7 @@ def create_zone(name, view_name) -> dns.zone.Zone:
|
||||
ns_rdataset = _create_ns_rdataset()
|
||||
zone_root_node.rdatasets.append(ns_rdataset)
|
||||
|
||||
# version.catz. record
|
||||
# version.<catalog zone name>. record
|
||||
version_rdataset = _create_version_rdataset()
|
||||
zone_version_node = zone.find_node(
|
||||
dns.name.from_text("version", origin), create=True
|
||||
@@ -158,6 +184,32 @@ def update_member(zone: NBZone) -> None:
|
||||
close_old_connections()
|
||||
|
||||
|
||||
def schedule_catalog_zone_notify(view: NBView) -> None:
|
||||
"""Schedule a NOTIFY for the catalog zone of a view.
|
||||
|
||||
Notifies transfer clients for the single configured catalog zone name
|
||||
(see ``catalog_zone_name``). The job receives the name as text, since
|
||||
job kwargs are serialized to JSON.
|
||||
"""
|
||||
if not SETTINGS.get("notify_clients", False):
|
||||
return
|
||||
|
||||
try:
|
||||
catalog_zone = view.catalog_zone
|
||||
except CatalogZone.DoesNotExist:
|
||||
return
|
||||
|
||||
notify.SendClientNotify.enqueue(
|
||||
zone_name=catalog_zone_name(view.name).to_text(omit_final_dot=True),
|
||||
soa_serial=catalog_zone.soa_serial,
|
||||
view_name=view.name,
|
||||
soa_refresh=catalog_zone.soa_refresh,
|
||||
soa_retry=catalog_zone.soa_retry,
|
||||
soa_expire=catalog_zone.soa_expire,
|
||||
soa_minimum=catalog_zone.soa_minimum,
|
||||
)
|
||||
|
||||
|
||||
def _create_soa_rdataset(view: NBView) -> dns.rdataset:
|
||||
try:
|
||||
serial_obj, _ = CatalogZone.objects.get_or_create(
|
||||
|
||||
@@ -289,9 +289,8 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
|
||||
return
|
||||
|
||||
# Check if catalog zone
|
||||
dname_lower = dname.lower()
|
||||
if dname_lower == "catz" or dname_lower == f"{nb_view.name.lower()}.catz":
|
||||
zone = catzm.create_zone(dname_lower, nb_view.name)
|
||||
if catzm.is_catalog_zone_query(qname, nb_view.name):
|
||||
zone = catzm.create_zone(nb_view.name)
|
||||
else:
|
||||
zone = self._get_zone_from_nb(dname, nb_view.name)
|
||||
# When zone was not found, let client know
|
||||
|
||||
Reference in new issue
Block a user