fix: never lower a serial or cap it at 99
This commit is contained in:
5 files changed
+74
-39
No files matched your search
+9
-7
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
@@ -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
|
||||||
Reference in new issue
Block a user