"""The dhcpd.conf and reverse zones generated from the test_data fixture.""" import pytest from django.core.management import call_command from freezegun import freeze_time from mdb.models import DhcpConfig, Interface, Ip4Subnet, Ip6Subnet from mdb.services.zone_sync import ZoneSyncService EXPECTED_DHCPD_CONF = """\ # Autogenerated configuration 2024-01-01 00:00:00 default-lease-time 600; max-lease-time 7200; log-facility local7; ddns-update-style none; # clients subnet 10.0.3.0 netmask 255.255.255.0 { \toption domain-name "edb.technology"; \toption domain-name-servers 158.36.190.194, 158.36.190.199, 8.8.8.8; \toption routers 158.36.186.1; \tdeny unknown-clients; \trange 10.0.3.2 10.0.3.254; } host tut { \thardware ethernet f0:de:f1:58:be:1a; \tfixed-address tut.edb.technology; \tfilename "pxelinux.0"; } """ SOA_AND_NS = """\ $TTL 3600 @ IN SOA sega.neuf.no. sysadmin.neuf.no. ( \t{serial}\t; serial \t28800\t; refresh \t7200\t; retry \t604800\t; expire \t86400 )\t; minimum ttl ; ; @\tIN\tNS\tsega.neuf.no. @\tIN\tNS\tatari.neuf.no. ; """ EXPECTED_IP4_REVERSE = ( SOA_AND_NS.format(serial=2024010101) + "2 \tIN\tPTR\ttut.edb.technology.\n" ) EXPECTED_IP6_REVERSE = ( SOA_AND_NS.format(serial=1) + "2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0\tPTR\ttut.edb.technology.\n" ) @pytest.fixture def fixtures(db): # Loading the interface bumps the subnet's serial to the frozen day's first. with freeze_time("2024-01-01 00:00:00"): call_command("loaddata", "test_data", verbosity=0) @pytest.fixture def service(tmp_path): svc = ZoneSyncService() svc.checkzone_dir = str(tmp_path) return svc def body(zone_file): """A zone file without its header comments, which name the file and the time.""" return zone_file[zone_file.index("$TTL") :] @freeze_time("2024-01-01 00:00:00") def test_dhcpd_configuration(fixtures): assert DhcpConfig.objects.get().dhcpd_configuration() == EXPECTED_DHCPD_CONF @freeze_time("2024-01-01 00:00:00") def test_dhcpd_configuration_authoritative(fixtures): DhcpConfig.objects.update(authoritative=True) conf = DhcpConfig.objects.get().dhcpd_configuration() assert conf.splitlines()[1] == "authoritative;" def test_dhcpd_configuration_leaves_out_non_dhcp_hosts(fixtures): Interface.objects.update(dhcp_client=False) assert "host tut" not in DhcpConfig.objects.get().dhcpd_configuration() def test_dhcpd_configuration_without_pxe_filename(fixtures): Interface.objects.update(pxe_filename="") conf = DhcpConfig.objects.get().dhcpd_configuration() assert "host tut {" in conf assert "filename" not in conf def test_dhcpd_configuration_without_dynamic_range(fixtures): Ip4Subnet.objects.update(dhcp_dynamic=False) assert "range" not in DhcpConfig.objects.get().dhcpd_configuration() def test_ip4_reverse_zone(fixtures, service): zone = Ip4Subnet.objects.get() contents = zone.zone_file_contents() assert body(contents) == EXPECTED_IP4_REVERSE status, output = service.check_zone(zone, contents) assert status == 0, output def test_ip4_reverse_zone_leaves_out_unassigned_addresses(fixtures): Interface.objects.update(ip4address=None) assert "PTR" not in Ip4Subnet.objects.get().zone_file_contents() def test_ip6_reverse_zone(fixtures, service): zone = Ip6Subnet.objects.get() contents = zone.zone_file_contents() assert body(contents) == EXPECTED_IP6_REVERSE status, output = service.check_zone(zone, contents) assert status == 0, output def test_ip6_reverse_zone_name(): zone = Ip6Subnet(network="2001:16d8:eeb6:3", netmask=64) assert zone.reverse_zone_name == "3.0.0.0.6.b.e.e.8.d.6.1.1.0.0.2.ip6.arpa"