From 690dc3a4f9334431ea480ffc6f4df880405c5e40 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sat, 20 Apr 2024 21:23:53 +0200 Subject: [PATCH] Enter NscZoneConfig --- nsc.py | 101 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/nsc.py b/nsc.py index ca4c33c..b734250 100755 --- a/nsc.py +++ b/nsc.py @@ -35,11 +35,11 @@ class NscNode: self.nsc_zone = nsc_zone self.name = name self.node = nsc_zone.zone.find_node(name, create=True) - self._ttl = int(nsc_zone.min_ttl.total_seconds()) + self._ttl = nsc_zone._min_ttl def ttl(self, *args, **kwargs) -> Self: if not args and not kwargs: - self._ttl = int(self.nsc_zone.min_ttl.total_seconds()) + self._ttl = self.nsc_zone._min_ttl else: self._ttl = int(timedelta(*args, **kwargs).total_seconds()) return self @@ -101,51 +101,75 @@ class NscNode: return self -class NscZone: - name: str - admin_email: Optional[str] = None - refresh: timedelta = timedelta(hours=8) - retry: timedelta = timedelta(hours=2) - expire: timedelta = timedelta(days=14) - min_ttl: timedelta = timedelta(days=1) - origin_server: Optional[str] = None - zone: Zone +class NscZoneConfig: + admin_email: str + refresh: timedelta + retry: timedelta + expire: timedelta + min_ttl: timedelta + origin_server: str + + default_config: Optional['NscZoneConfig'] = None def __init__(self, - name: str, admin_email: Optional[str] = None, refresh: Optional[timedelta] = None, retry: Optional[timedelta] = None, expire: Optional[timedelta] = None, min_ttl: Optional[timedelta] = None, origin_server: Optional[str] = None, + inherit_config: Optional['NscZoneConfig'] = None, ) -> None: - self.name = name - self.admin_email = admin_email if admin_email is not None else self.admin_email - self.refresh = refresh if refresh is not None else self.refresh - self.retry = retry if retry is not None else self.retry - self.expire = expire if expire is not None else self.expire - self.min_ttl = min_ttl if min_ttl is not None else self.min_ttl - self.origin_server = origin_server if origin_server is not None else self.origin_server - self.zone = dns.zone.Zone(origin=name, rdclass=RdataClass.IN) - - if self.origin_server is None: + if inherit_config is None: + inherit_config = NscZoneConfig.default_config or self # to satisfy the type checker + self.admin_email = admin_email if admin_email is not None else inherit_config.admin_email + self.refresh = refresh if refresh is not None else inherit_config.refresh + self.retry = retry if retry is not None else inherit_config.retry + self.expire = expire if expire is not None else inherit_config.expire + self.min_ttl = min_ttl if min_ttl is not None else inherit_config.min_ttl + self.origin_server = origin_server if origin_server is not None else inherit_config.origin_server + + def finalize(self) -> Self: + if not self.origin_server: self.origin_server = socket.getfqdn() + if not self.admin_email: + self.admin_email = f'hostmaster@{self.origin_server}' + return self + - if self.admin_email is None: - self.admin_email = f'root@{self.origin_server}' +NscZoneConfig.default_config = NscZoneConfig( + admin_email="", + refresh=timedelta(hours=8), + retry=timedelta(hours=2), + expire=timedelta(days=14), + min_ttl=timedelta(days=1), + origin_server="", +) + + +class NscZone: + name: str + zone: Zone + _min_ttl: int + + def __init__(self, name: str, **kwargs) -> None: + self.name = name + self.config = NscZoneConfig(**kwargs).finalize() + self.zone = dns.zone.Zone(origin=name, rdclass=RdataClass.IN) + self._min_ttl = int(self.config.min_ttl.total_seconds()) + conf = self.config root = self[""] root._add( dns.rdtypes.ANY.SOA.SOA( RdataClass.IN, RdataType.SOA, - mname=self.origin_server, - rname=self.admin_email.replace('@', '.'), # FIXME: names with dots + mname=conf.origin_server, + rname=conf.admin_email.replace('@', '.'), # FIXME: names with dots serial=12345, - refresh=int(self.refresh.total_seconds()), - retry=int(self.retry.total_seconds()), - expire=int(self.expire.total_seconds()), - minimum=int(self.min_ttl.total_seconds()), + refresh=int(conf.refresh.total_seconds()), + retry=int(conf.retry.total_seconds()), + expire=int(conf.expire.total_seconds()), + minimum=int(conf.min_ttl.total_seconds()), ) ) @@ -163,24 +187,27 @@ class NscZone: def dump(self) -> None: # Could use self.zone.to_file(sys.stdout), but we want better formatting last_name = None - min_ttl = int(self.min_ttl.total_seconds()) for name, ttl, rec in self.zone.iterate_rdatas(): if name == last_name: print_name = "" else: print_name = name - print(f'{print_name}\t{ttl if ttl != min_ttl else ""}\t{rec.rdtype.name}\t{rec.to_text()}') + print(f'{print_name}\t{ttl if ttl != self._min_ttl else ""}\t{rec.rdtype.name}\t{rec.to_text()}') last_name = name -class Config: +class Nsc: zones: Dict[str, Zone] + default_zone_config: NscZoneConfig - def __init__(self) -> None: + def __init__(self, **kwargs) -> None: self.zones = {} + self.default_zone_config = NscZoneConfig(**kwargs) - def add_zone(self, *args, **kwargs) -> Zone: - dom = NscZone(*args, **kwargs) + def add_zone(self, *args, inherit_config: Optional[NscZoneConfig] = None, **kwargs) -> Zone: + if inherit_config is None: + inherit_config = self.default_zone_config + dom = NscZone(*args, inherit_config=inherit_config, **kwargs) assert dom.name not in self.zones self.zones[dom.name] = dom return dom @@ -191,7 +218,7 @@ class MyZone(Zone): origin_server = 'ns.ucw.cz' -c = Config() +c = Nsc() z = c.add_zone('ucw.cz') # origin_server='jabberwock.ucw.cz') z[""].NS(['jabberwock', 'chirigo.gebbeth.cz', 'drak.ucw.cz']) -- 2.39.5