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
+73 -38

No files matched your search

+9 -7
View File
@@ -10,8 +10,8 @@ class SerialNumberInput(NumberInput):
"""Number input for DNS SOA serials (YYYYMMDDXX).
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
counter (incrementing if the field already holds today's serial).
that prefills the field with the next serial: today's first, or one more than
the current serial when that is higher.
"""
def __init__(self, attrs=None):
@@ -35,12 +35,14 @@ if (typeof window.mdbBumpSerial === 'undefined') {
var today = '' + now.getFullYear()
+ String(now.getMonth() + 1).padStart(2, '0')
+ String(now.getDate()).padStart(2, '0');
var current = (input.value || '').trim();
var xx = 1;
if (current.length === 10 && current.slice(0, 8) === today) {
xx = Math.min(parseInt(current.slice(8), 10) + 1, 99);
// As mdb.utils.format_domain_serial_and_add_one: never lower than the current
// serial, so past XX = 99 it carries into the date.
var current = parseInt((input.value || '').trim(), 10);
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>
+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):
if instance.domain is not None:
domain = instance.domain
domain.domain_serial += 1
domain.domain_serial = format_domain_serial_and_add_one(domain.domain_serial)
domain.save()
if instance.ip4address is not None:
subnet = instance.ip4address.subnet
subnet.domain_serial += 1
subnet.domain_serial = format_domain_serial_and_add_one(subnet.domain_serial)
subnet.save()
+10 -28
View File
@@ -1,34 +1,16 @@
import datetime
import re
def format_domain_serial_and_add_one(serial):
today = datetime.datetime.now()
res = re.findall(
rf"^{today.year:4d}{today.month:02d}{today.day:02d}(\d\d)$",
str(serial),
re.DOTALL,
)
"""Return the serial after `serial`, in the YYYYMMDDnn form where it can be.
if len(res) == 0:
""" This probably means that the serial is malformed
or the date is wrong. We assume that if the date is wrong,
it is in the past. Just create a new serial starting from 1."""
return f"{today.year:4d}{today.month:02d}{today.day:02d}01"
elif len(res) == 1:
""" The serial contains todays date, just update it. """
The serial must always increase, or named and dhcpd ignore the change. Today's first
serial is used while it is higher. Otherwise the serial goes up by one: past nn = 99
it carries into the date (2026110299 -> 2026110300), and a serial already ahead of
today, from that carry or a dynamic update, keeps counting from where it is.
"""
today_first = int(datetime.date.today().strftime("%Y%m%d")) * 100 + 1
try:
number = int(res[0])
except ValueError:
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"
return max(int(serial) + 1, today_first)
except TypeError, ValueError:
return today_first
+20 -1
View File
@@ -1,6 +1,7 @@
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
@@ -65,3 +66,21 @@ def test_ip6_signal_sets_domain_name():
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"
# ---------------------------------------------------------------------------
# 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