]> mj.ucw.cz Git - pynsc.git/commitdiff
More daemon configuration
authorMartin Mares <mj@ucw.cz>
Sun, 21 Apr 2024 14:41:31 +0000 (16:41 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 21 Apr 2024 14:41:31 +0000 (16:41 +0200)
example/__init__.py
nsconfig/cli.py
nsconfig/core.py
nsconfig/daemon/__init__.py
nsconfig/daemon/bind.py

index aa37ecaf53b43f663acec63b4c3489541a1cbc35..d7dd2192ad1344615c8772b742511d51b93798aa 100644 (file)
@@ -4,7 +4,7 @@ from nsconfig.daemon.bind import NscDaemonBind
 nsc = Nsc(
     admin_email='admin@example.org',
     origin_server='ns.example.org',
-    daemon=NscDaemonBind(),
+    daemon=NscDaemonBind(control_command='echo'),
 )
 
 for rev in ['10.1.0.0/16', '10.2.0.0/16', 'fd12:3456:789a::/48']:
index 633a47e1b7b4d85bc2c26861f01958c5d61f57d5..84b2d08031021d09ee81515e9e53694ce737a958 100644 (file)
@@ -58,12 +58,17 @@ def do_status(nsc: Nsc) -> None:
 
 
 def do_update(nsc: Nsc) -> None:
+    nsc.daemon.write_config()
+
     for z in nsc.get_zones():
         if isinstance(z, NscZonePrimary) and z.is_changed():
             print(f'Updating zone {z.name} (serial {z.state.serial})')
             z.write_zone()
+            nsc.daemon.reload_zone(z)
             z.write_state()
 
+    nsc.daemon.reload_daemon()
+
 
 def main(nsc: Nsc) -> None:
     parser = argparse.ArgumentParser(description='Configure name server')
index b7d7de47852efae79322016a3c313d726afd831b..8a3bb74f694453655887758f39063fa20182779e 100644 (file)
@@ -356,7 +356,7 @@ class Nsc:
     state_dir: Path
     zone_dir: Path
     secondary_dir: Path
-    daemon: Optional['NscDaemon']  # Set by DaemonConfig class
+    daemon: 'NscDaemon'  # Set by DaemonConfig class
 
     def __init__(self,
                  directory: str = '.',
@@ -376,9 +376,11 @@ class Nsc:
         self.secondary_dir = self.root_dir / 'secondary'
         self.secondary_dir.mkdir(parents=True, exist_ok=True)
 
+        if daemon is None:
+            from nsconfig.daemon import NscDaemonNull
+            daemon = NscDaemonNull()
         self.daemon = daemon
-        if daemon is not None:
-            daemon.setup(self)
+        daemon.setup(self)
 
     def add_zone(self,
                  name: Optional[str] = None,
index a981a0c22dbc676f5de931849d04b3d5ebc6fbdb..914056100d36340a051788049b2bd7696d78f785 100644 (file)
@@ -1,16 +1,63 @@
+from io import StringIO
 from pathlib import Path
 from typing import TextIO
+import subprocess
 import sys
 
-from nsconfig.core import Nsc
+from nsconfig.core import Nsc, NscZone
 
 
 class NscDaemon:
     nsc: Nsc
-    config_path: Path
+
+    def __init__(self) -> None:
+        pass
 
     def setup(self, nsc: Nsc) -> None:
         self.nsc = nsc
 
     def dump_config(self, file: TextIO = sys.stdout) -> None:
         pass
+
+    def write_config(self) -> None:
+        pass
+
+    def reload_zone(self, z: NscZone) -> None:
+        pass
+
+    def reload_daemon(self) -> None:
+        pass
+
+    def _install_config(self, path: Path, new_contents: str) -> bool:
+        try:
+            old_new_contents = path.read_text()
+        except FileNotFoundError:
+            old_new_contents = None
+        if new_contents == old_new_contents:
+            return False
+        else:
+            new_path = Path(str(path) + '.new')
+            with open(new_path, 'w') as f:
+                f.write(new_contents)
+            new_path.replace(path)
+            return True
+
+    def _write_config(self, config_path: Path) -> bool:
+        string_stream = StringIO()
+        self.dump_config(string_stream)
+        if self._install_config(config_path, string_stream.getvalue()):
+            print('Wrote new daemon configuration')
+            return True
+        else:
+            print('Daemon configuration not changed')
+            return False
+
+    def _run_command(self, argv, **kwargs) -> None:
+        res = subprocess.run(argv, **kwargs)
+        if res.returncode > 0:
+            print(f'Command failed: {argv}')
+            sys.exit(1)
+
+
+class NscDaemonNull(NscDaemon):
+    pass
index 13a9e0c1087f2153b936b8d0c4c03bc6869891cf..24a24fca8ae49200edf15467c96c190097fdd7fb 100644 (file)
@@ -2,12 +2,22 @@ from pathlib import Path
 import sys
 from typing import TextIO
 
-from nsconfig.core import NscZonePrimary, NscZoneSecondary
+from nsconfig.core import NscZone, NscZonePrimary, NscZoneSecondary
 from nsconfig.daemon import NscDaemon
 
 
 class NscDaemonBind(NscDaemon):
-    config_path: Path = Path('named.conf.nsc')
+    config_path: Path
+    control_command: str
+    need_full_reload: bool
+
+    def __init__(self,
+                 config_file: str = 'named.conf.nsc',
+                 control_command: str = 'rndc') -> None:
+        super().__init__()
+        self.config_path = Path(config_file)
+        self.control_command = control_command
+        self.need_full_reload = False
 
     def dump_config(self, file: TextIO = sys.stdout) -> None:
         file.write('# Domains managed by NSC\n')
@@ -24,3 +34,17 @@ class NscDaemonBind(NscDaemon):
             else:
                 raise NotImplementedError()
             file.write('}\n\n')
+
+    def write_config(self) -> None:
+        if self._write_config(self.config_path):
+            self.need_full_reload = True
+
+    def reload_zone(self, z: NscZone) -> None:
+        if isinstance(z, NscZonePrimary) and not self.need_full_reload:
+            print(f'Reloading zone {z.name}')
+            self._run_command([self.control_command, 'reload', z.name])
+
+    def reload_daemon(self) -> None:
+        if self.need_full_reload:
+            print('Reloading daemon')
+            self._run_command([self.control_command, 'reload'])