]> mj.ucw.cz Git - pynsc.git/commitdiff
First bits of daemon configuration
authorMartin Mares <mj@ucw.cz>
Sun, 21 Apr 2024 09:48:14 +0000 (11:48 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 21 Apr 2024 14:06:32 +0000 (16:06 +0200)
TODO
example/__init__.py
nsconfig/cli.py
nsconfig/core.py
nsconfig/daemon/__init__.py [new file with mode: 0644]
nsconfig/daemon/bind.py [new file with mode: 0644]

diff --git a/TODO b/TODO
index afd48e87d8419f9dbf0fe500a682ba800c2016ab..cd0f4bbf3b1d492e65086f79f8edf142b8efb45f 100644 (file)
--- a/TODO
+++ b/TODO
@@ -6,3 +6,4 @@
 - DNSSEC
 - Auto-generated files should contain a comment saying so
 - Automated generation of Null MX
+- Bind: custom config
index 6fd7fb5ecc3af08d1a50a0a27d623e53cbdf2cb4..aa37ecaf53b43f663acec63b4c3489541a1cbc35 100644 (file)
@@ -1,8 +1,10 @@
 from nsconfig import Nsc
+from nsconfig.daemon.bind import NscDaemonBind
 
 nsc = Nsc(
     admin_email='admin@example.org',
     origin_server='ns.example.org',
+    daemon=NscDaemonBind(),
 )
 
 for rev in ['10.1.0.0/16', '10.2.0.0/16', 'fd12:3456:789a::/48']:
index 9d342c071ab232256db4c2f2add91a252af4a992..633a47e1b7b4d85bc2c26861f01958c5d61f57d5 100644 (file)
@@ -2,7 +2,7 @@ import argparse
 from pathlib import Path
 from texttable import Texttable
 
-from nsconfig.core import Nsc, ZoneType
+from nsconfig.core import Nsc, NscZonePrimary, NscZoneSecondary
 
 
 def do_test(nsc: Nsc) -> None:
@@ -11,7 +11,7 @@ def do_test(nsc: Nsc) -> None:
     for z in nsc.get_zones():
         print(f'Zone:        {z.name}')
         print(f'Type:        {z.zone_type.name}')
-        if z.zone_type == ZoneType.primary:
+        if isinstance(z, NscZonePrimary):
             print(f'Old serial:  {z.prev_state.serial}')
             print(f'Old hash:    {z.prev_state.hash}')
             print(f'New serial:  {z.state.serial}')
@@ -20,10 +20,16 @@ def do_test(nsc: Nsc) -> None:
             print(f'Dumping to:  {out_file}')
             with open(out_file, 'w') as f:
                 z.dump(file=f)
-        else:
+        elif isinstance(z, NscZoneSecondary):
             print(f'Primary:     {z.primary_server}')
         print()
 
+    if nsc.daemon:
+        conf_file = test_dir / nsc.daemon.config_path.name
+        print(f'Dumping daemon config to {conf_file}')
+        with open(conf_file, 'w') as f:
+            nsc.daemon.dump_config(file=f)
+
 
 def do_status(nsc: Nsc) -> None:
     table = Texttable(max_width=0)
@@ -32,13 +38,13 @@ def do_status(nsc: Nsc) -> None:
     table.set_deco(Texttable.HEADER)
 
     for z in nsc.get_zones():
-        if z.zone_type != ZoneType.primary:
+        if not isinstance(z, NscZonePrimary):
             table.add_row([z.name, 'secondary', "", "", "", ""])
             continue
-        if z.state.serial == z.prev_state.serial:
-            action = ""
-        else:
+        if z.is_changed():
             action = '*'
+        else:
+            action = ""
         table.add_row([
             z.name,
             z.prev_state.serial,
@@ -53,7 +59,7 @@ def do_status(nsc: Nsc) -> None:
 
 def do_update(nsc: Nsc) -> None:
     for z in nsc.get_zones():
-        if z.zone_type == ZoneType.primary and z.state.serial != z.prev_state.serial:
+        if isinstance(z, NscZonePrimary) and z.is_changed():
             print(f'Updating zone {z.name} (serial {z.state.serial})')
             z.write_zone()
             z.write_state()
index b61a02d36f181d2826740df6f257f25fa12c0faf..b7d7de47852efae79322016a3c313d726afd831b 100644 (file)
@@ -21,7 +21,11 @@ import json
 from pathlib import Path
 import socket
 import sys
-from typing import Optional, Dict, List, Self, Tuple, DefaultDict, TextIO
+from typing import Optional, Dict, List, Self, Tuple, DefaultDict, TextIO, TYPE_CHECKING
+
+
+if TYPE_CHECKING:
+    from nsconfig.daemon import NscDaemon
 
 
 IPAddress = IPv4Address | IPv6Address
@@ -327,6 +331,9 @@ class NscZonePrimary(NscZone):
     def write_state(self) -> None:
         self.state.save(self.state_file)
 
+    def is_changed(self) -> bool:
+        return self.state.serial != self.prev_state.serial
+
 
 class NscZoneSecondary(NscZone):
     primary_server: IPAddress
@@ -349,8 +356,12 @@ class Nsc:
     state_dir: Path
     zone_dir: Path
     secondary_dir: Path
+    daemon: Optional['NscDaemon']  # Set by DaemonConfig class
 
-    def __init__(self, directory: str = '.', **kwargs) -> None:
+    def __init__(self,
+                 directory: str = '.',
+                 daemon: Optional['NscDaemon'] = None,
+                 **kwargs) -> None:
         self.start_time = datetime.now()
         self.zones = {}
         self.default_zone_config = NscZoneConfig(**kwargs)
@@ -365,6 +376,10 @@ class Nsc:
         self.secondary_dir = self.root_dir / 'secondary'
         self.secondary_dir.mkdir(parents=True, exist_ok=True)
 
+        self.daemon = daemon
+        if daemon is not None:
+            daemon.setup(self)
+
     def add_zone(self,
                  name: Optional[str] = None,
                  reverse_for: str | IPNetwork | None = None,
diff --git a/nsconfig/daemon/__init__.py b/nsconfig/daemon/__init__.py
new file mode 100644 (file)
index 0000000..a981a0c
--- /dev/null
@@ -0,0 +1,16 @@
+from pathlib import Path
+from typing import TextIO
+import sys
+
+from nsconfig.core import Nsc
+
+
+class NscDaemon:
+    nsc: Nsc
+    config_path: Path
+
+    def setup(self, nsc: Nsc) -> None:
+        self.nsc = nsc
+
+    def dump_config(self, file: TextIO = sys.stdout) -> None:
+        pass
diff --git a/nsconfig/daemon/bind.py b/nsconfig/daemon/bind.py
new file mode 100644 (file)
index 0000000..13a9e0c
--- /dev/null
@@ -0,0 +1,26 @@
+from pathlib import Path
+import sys
+from typing import TextIO
+
+from nsconfig.core import NscZonePrimary, NscZoneSecondary
+from nsconfig.daemon import NscDaemon
+
+
+class NscDaemonBind(NscDaemon):
+    config_path: Path = Path('named.conf.nsc')
+
+    def dump_config(self, file: TextIO = sys.stdout) -> None:
+        file.write('# Domains managed by NSC\n')
+        file.write('# This file was automatically generated by NSC, please do not edit manually.\n\n')
+        for z in self.nsc.get_zones():
+            file.write(f'zone "{z.name}" in {{\n')  # broken editor: }}
+            if isinstance(z, NscZonePrimary):
+                file.write('\ttype master;\n')
+                file.write(f'\tfile "{z.zone_file}";\n')
+            elif isinstance(z, NscZoneSecondary):
+                file.write('\ttype slave;\n')
+                file.write(f'\tfile "zone/{z.secondary_file}";\n')
+                file.write(f'\tmasters {{ {z.primary_server}; }};\n')
+            else:
+                raise NotImplementedError()
+            file.write('}\n\n')