Address review: rename settings to notify_ns_* and add global toggle

Per PR review feedback:
- Rename settings: custom_field_force_notify -> notify_ns_custom_field_name,
  force_notify_port -> notify_ns_port.
- Add notify_ns_all_zones (bool, default False) to enable the feature globally
  for every zone; it coexists with the per-zone custom field — a NOTIFY is sent
  if the global switch is on OR the zone's custom field is set.
- Rename the internal job/scheduler/log strings to the notify_ns naming for
  consistency (SendZoneNSNotify, schedule_notify_ns).
- Update README and CHANGELOG.
This commit is contained in:
d_ev committed 2026-07-16 14:57:14 +03:00
1 parent dc1c223400
commit 4ad41360de
4 files changed
+61 -41

No files matched your search

+6 -6
View File
@@ -1,10 +1,10 @@
## Unreleased
- Add `custom_field_force_notify` setting. When set to the name of a boolean custom field on the
NetBox DNS Zone object, the plugin sends a NOTIFY directly to a zone's own nameservers whenever
the zone has that custom field set to `True` and its SOA serial number changes. Nameserver
addresses are resolved from NetBox DNS A/AAAA records within the zone's view and the NOTIFY is
signed with the per-view TSIG key. A companion `force_notify_port` setting (default 53) controls
the destination port.
- Add the option to send a NOTIFY directly to a zone's own nameservers when its SOA serial number
changes. Enable it per zone via a boolean custom field named by `notify_ns_custom_field_name`,
globally for all zones via `notify_ns_all_zones` (default `False`), or both. Nameserver addresses
are resolved from NetBox DNS A/AAAA records within the zone's view and the NOTIFY is signed with
the per-view TSIG key. A companion `notify_ns_port` setting (default 53) controls the destination
port.
## 1.0.7 - 2026-03-02
README Change - Moving private keys to global scope since Bind 9.20 view scoped keys have become unreliable and sometimes wouldnt match.
+24 -15
View File
@@ -98,22 +98,31 @@ list of clients to be notified on zone changes. Default is 24 hours.
```
Switch to using TCP instead of UDP. Defaults to False
#### Forced NOTIFY to a zone's nameservers
#### NOTIFY to a zone's own nameservers
In addition to notifying clients that queried the transfer endpoint, the plugin can send a NOTIFY
directly to a zone's own nameservers whenever that zone's SOA serial number changes. This is useful
when you want specific zones to have their nameservers pull updates immediately, independent of the
`notify_clients` mechanism.
The feature is driven by a boolean custom field on the NetBox DNS Zone object. Create a boolean
custom field (e.g. `dns_bridge_force_notify_ns`) that applies to `netbox_dns > zone`, then point the
plugin at it:
```
'custom_field_force_notify': 'dns_bridge_force_notify_ns'
```
When this setting is present, every time a zone whose custom field is set to `True` changes its SOA
serial number, the plugin schedules a background NOTIFY to each of the zone's configured nameservers.
when you want nameservers to pull updates immediately, independent of the `notify_clients` mechanism.
Only a change of the SOA serial triggers this — other zone edits that leave the serial untouched do
not. The setting is unset by default, which disables the feature entirely.
not.
This can be enabled per zone, globally, or both. If neither of the settings below is configured, the
feature is disabled entirely.
**Per zone (custom field).** Create a boolean custom field (e.g. `dns_bridge_notify_ns`) that applies
to `netbox_dns > zone`, then point the plugin at it:
```
'notify_ns_custom_field_name': 'dns_bridge_notify_ns'
```
A NOTIFY is then sent for any zone whose custom field is set to `True`. This lets you toggle the
behavior per zone straight from the NetBox UI, without changing the plugin config.
**Globally (all zones).**
```
'notify_ns_all_zones': True
```
When enabled, a NOTIFY is sent for every zone on serial change, regardless of the custom field.
Defaults to `False`. The two options coexist: a NOTIFY is sent if the global switch is on **or** the
zone's custom field is set.
The nameserver addresses are resolved from NetBox DNS itself by looking up active A/AAAA records
(within the zone's view) matching each nameserver's hostname. The NOTIFY messages are signed with the
@@ -121,9 +130,9 @@ same per-view TSIG key configured under `tsig_keys`, so the receiving nameserver
signed with that key (e.g. BIND's `allow-notify { key "..."; };`).
```
'force_notify_port': 53
'notify_ns_port': 53
```
Destination port used for these forced NOTIFY messages. Defaults to 53.
Destination port used for these NOTIFY messages. Defaults to 53.
As with the client NOTIFY feature, this uses NetBox's background job system, so at least one
`rq-worker` service must be running.
+13 -13
View File
@@ -111,7 +111,7 @@ class SendDNSNotify(JobRunner):
close_old_connections()
class SendForcedNSNotify(JobRunner):
class SendZoneNSNotify(JobRunner):
"""Send a NOTIFY straight to a zone's own nameservers.
Unlike ``SendDNSNotify`` (which notifies clients that previously queried the
@@ -121,11 +121,11 @@ class SendForcedNSNotify(JobRunner):
"""
class Meta:
name = "Send forced DNS NOTIFY to zone nameservers"
name = "Send DNS NOTIFY to zone nameservers"
def run(self, *args, **kwargs):
notify_over_tcp = SETTINGS.get("notify_over_tcp", False)
port = SETTINGS.get("force_notify_port", 53)
port = SETTINGS.get("notify_ns_port", 53)
view_name = kwargs["view_name"]
zone_name = kwargs["zone_name"]
soa_serial = kwargs["soa_serial"]
@@ -133,17 +133,17 @@ class SendForcedNSNotify(JobRunner):
try:
zone = Zone.objects.get(pk=kwargs["zone_id"])
except Zone.DoesNotExist:
LOGGER.error(f"Forced NOTIFY: zone id {kwargs['zone_id']} no longer exists")
LOGGER.error(f"NS NOTIFY: zone id {kwargs['zone_id']} no longer exists")
return
except OperationalError as e:
LOGGER.error(f"Forced NOTIFY failed due to unexpected error: {e}")
LOGGER.error(f"NS NOTIFY failed due to unexpected error: {e}")
close_old_connections()
return
ns_targets = _resolve_zone_nameserver_ips(zone)
if not ns_targets:
LOGGER.warning(
f"Forced NOTIFY: no nameserver addresses found in NetBox DNS for "
f"NS NOTIFY: no nameserver addresses found in NetBox DNS for "
f"{view_name}/{zone_name} — skipping"
)
return
@@ -152,7 +152,7 @@ class SendForcedNSNotify(JobRunner):
tsig_key = _load_tsig_key(view_name)
except RuntimeError as e:
LOGGER.error(
f"Forced NOTIFY aborted for {view_name}/{zone_name}: {e}"
f"NS NOTIFY aborted for {view_name}/{zone_name}: {e}"
)
return
@@ -172,18 +172,18 @@ class SendForcedNSNotify(JobRunner):
for ip in ns_targets:
try:
if notify_over_tcp:
LOGGER.debug("Sending forced NOTIFY over TCP")
LOGGER.debug("Sending NS NOTIFY over TCP")
dns.query.tcp(msg, ip, port=port, timeout=2)
else:
LOGGER.debug("Sending forced NOTIFY over UDP")
LOGGER.debug("Sending NS NOTIFY over UDP")
dns.query.udp(msg, ip, port=port, timeout=2)
LOGGER.info(
f"Forced NOTIFY sent: {ip} {view_name}/{fqdn} {soa_serial}"
f"NS NOTIFY sent: {ip} {view_name}/{fqdn} {soa_serial}"
)
except Exception as e:
LOGGER.error(
f"Forced NOTIFY failed: {ip} {view_name}/{fqdn} {soa_serial}: {e}"
f"NS NOTIFY failed: {ip} {view_name}/{fqdn} {soa_serial}: {e}"
)
@@ -253,12 +253,12 @@ def schedule(zone: Zone):
)
def schedule_force_ns_notify(zone: Zone):
def schedule_notify_ns(zone: Zone):
soa_serial = _get_soa_serial(zone)
if soa_serial is None:
return
SendForcedNSNotify.enqueue(
SendZoneNSNotify.enqueue(
zone_id=zone.pk,
zone_name=zone.name,
view_name=zone.view.name,
+18 -7
View File
@@ -54,11 +54,10 @@ def zone_post_save(sender, instance, created, **kwargs):
if SETTINGS.get("notify_clients", False):
notify_handler.schedule(zone)
# Force a NOTIFY to the zone's own nameservers when the zone carries the
# configured boolean custom field set to True and its SOA serial changed.
cf_name = SETTINGS.get("custom_field_force_notify")
if cf_name and soa_serial_changed and _force_notify_enabled(zone, cf_name):
notify_handler.schedule_force_ns_notify(zone)
# Send a NOTIFY to the zone's own nameservers when its SOA serial changed
# and the feature is enabled — globally or per zone via a custom field.
if soa_serial_changed and _notify_ns_enabled(zone):
notify_handler.schedule_notify_ns(zone)
transaction.on_commit(_on_commit)
@@ -77,7 +76,19 @@ def zone_post_delete(sender, instance, **kwargs):
catzm.increment_soa_serial()
def _force_notify_enabled(zone, cf_name: str) -> bool:
"""Return True only if the zone's boolean custom field ``cf_name`` is True."""
def _notify_ns_enabled(zone) -> bool:
"""Return True if a NOTIFY to the zone's nameservers should be sent.
Enabled either globally for every zone via ``notify_ns_all_zones`` or per
zone when the boolean custom field named by ``notify_ns_custom_field_name``
is set to True on the zone.
"""
if SETTINGS.get("notify_ns_all_zones", False):
return True
cf_name = SETTINGS.get("notify_ns_custom_field_name")
if not cf_name:
return False
custom_fields = getattr(zone, "custom_field_data", None) or {}
return custom_fields.get(cf_name) is True