fix: never lower a serial or cap it at 99

This commit is contained in:
ponas committed 2026-11-02 01:31:03 +01:00
1 parent 58937e1105
commit 3b589f6085
5 files changed
+74 -39

No files matched your search

+9 -7
View File
@@ -10,8 +10,8 @@ class SerialNumberInput(NumberInput):
"""Number input for DNS SOA serials (YYYYMMDDXX). """Number input for DNS SOA serials (YYYYMMDDXX).
Renders wide enough to show the full 10-digit serial and adds a button Renders wide enough to show the full 10-digit serial and adds a button
that prefills the field with today's date plus the next available XX that prefills the field with the next serial: today's first, or one more than
counter (incrementing if the field already holds today's serial). the current serial when that is higher.
""" """
def __init__(self, attrs=None): def __init__(self, attrs=None):
@@ -35,12 +35,14 @@ if (typeof window.mdbBumpSerial === 'undefined') {
var today = '' + now.getFullYear() var today = '' + now.getFullYear()
+ String(now.getMonth() + 1).padStart(2, '0') + String(now.getMonth() + 1).padStart(2, '0')
+ String(now.getDate()).padStart(2, '0'); + String(now.getDate()).padStart(2, '0');
var current = (input.value || '').trim(); // As mdb.utils.format_domain_serial_and_add_one: never lower than the current
var xx = 1; // serial, so past XX = 99 it carries into the date.
if (current.length === 10 && current.slice(0, 8) === today) { var current = parseInt((input.value || '').trim(), 10);
xx = Math.min(parseInt(current.slice(8), 10) + 1, 99); var next = parseInt(today + '01', 10);
if (!isNaN(current) && current + 1 > next) {
next = current + 1;
} }
input.value = today + String(xx).padStart(2, '0'); input.value = String(next);
}; };
} }
</script> </script>
+2 -2
View File
@@ -136,12 +136,12 @@ def create_pxe_key_and_write_pxe_files_when_host_changes(
def update_domain_serial_when_interface_deleted(sender, instance, **kwargs): def update_domain_serial_when_interface_deleted(sender, instance, **kwargs):
if instance.domain is not None: if instance.domain is not None:
domain = instance.domain domain = instance.domain
domain.domain_serial += 1 domain.domain_serial = format_domain_serial_and_add_one(domain.domain_serial)
domain.save() domain.save()
if instance.ip4address is not None: if instance.ip4address is not None:
subnet = instance.ip4address.subnet subnet = instance.ip4address.subnet
subnet.domain_serial += 1 subnet.domain_serial = format_domain_serial_and_add_one(subnet.domain_serial)
subnet.save() subnet.save()
+11 -29
View File
@@ -1,34 +1,16 @@
import datetime import datetime
import re
def format_domain_serial_and_add_one(serial): def format_domain_serial_and_add_one(serial):
today = datetime.datetime.now() """Return the serial after `serial`, in the YYYYMMDDnn form where it can be.
res = re.findall(
rf"^{today.year:4d}{today.month:02d}{today.day:02d}(\d\d)$",
str(serial),
re.DOTALL,
)
if len(res) == 0: The serial must always increase, or named and dhcpd ignore the change. Today's first
""" This probably means that the serial is malformed serial is used while it is higher. Otherwise the serial goes up by one: past nn = 99
or the date is wrong. We assume that if the date is wrong, it carries into the date (2026110299 -> 2026110300), and a serial already ahead of
it is in the past. Just create a new serial starting from 1.""" today, from that carry or a dynamic update, keeps counting from where it is.
return f"{today.year:4d}{today.month:02d}{today.day:02d}01" """
elif len(res) == 1: today_first = int(datetime.date.today().strftime("%Y%m%d")) * 100 + 1
""" The serial contains todays date, just update it. """ try:
try: return max(int(serial) + 1, today_first)
number = int(res[0]) except TypeError, ValueError:
except ValueError: return today_first
number = 1
if number >= 99:
""" This is bad... Just keep the number on 99.
We also send a mail to sysadmins telling them that
something is wrong..."""
else:
number += 1
return f"{today.year:4d}{today.month:02d}{today.day:02d}{number:02d}"
else:
""" Just return the first serial for today. """
return f"{today.year:4d}{today.month:02d}{today.day:02d}01"
+20 -1
View File
@@ -1,6 +1,7 @@
import pytest import pytest
from freezegun import freeze_time
from mdb.models import DhcpConfig, Ip4Subnet, Ip6Subnet from mdb.models import DhcpConfig, Domain, Interface, Ip4Subnet, Ip6Subnet
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# pre_save signals: populate domain_name once, then leave it frozen # pre_save signals: populate domain_name once, then leave it frozen
@@ -65,3 +66,21 @@ def test_ip6_signal_sets_domain_name():
domain_filename="db.reverse-v6", domain_filename="db.reverse-v6",
) )
assert subnet.domain_name == "0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa" assert subnet.domain_name == "0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"
# ---------------------------------------------------------------------------
# pre_delete Interface: the zones it was in get a new serial
# ---------------------------------------------------------------------------
@freeze_time("2026-11-02")
def test_deleting_interface_raises_zone_serials(domain):
subnet = Interface.objects.get(pk=1).ip4address.subnet
Domain.objects.filter(pk=domain.pk).update(domain_serial=2026110299)
Ip4Subnet.objects.filter(pk=subnet.pk).update(domain_serial=2015010101)
Interface.objects.get(pk=1).delete()
# Past 99 the serial carries into the date instead of going back.
assert Domain.objects.get(pk=domain.pk).domain_serial == 2026110300
assert Ip4Subnet.objects.get(pk=subnet.pk).domain_serial == 2026110201
+32
View File
@@ -0,0 +1,32 @@
import pytest
from freezegun import freeze_time
from mdb.utils import format_domain_serial_and_add_one
@freeze_time("2026-11-02")
@pytest.mark.parametrize(
("serial", "expected"),
[
(2026110205, 2026110206), # today: next number
(2025010107, 2026110201), # an earlier day: today's first
(1, 2026110201), # not a date at all
(2026110298, 2026110299),
(2026110299, 2026110300), # used to stay at 99, so the change never synced
(2026110300, 2026110301), # ahead of today: never goes back
(2026110505, 2026110506),
(None, 2026110201),
("garbage", 2026110201),
],
)
def test_next_serial(serial, expected):
assert format_domain_serial_and_add_one(serial) == expected
@freeze_time("2026-11-02")
def test_serial_always_increases():
serial = 2026110290
for _ in range(30):
new = format_domain_serial_and_add_one(serial)
assert new > serial
serial = new