From: Martin Mares Date: Mon, 22 Apr 2024 12:21:11 +0000 (+0200) Subject: Improve status command X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=3acf954c8513246ea0e0b7ab867bb3a2285f95d3;p=pynsc.git Improve status command --- diff --git a/TODO b/TODO index 783852a..b4b14c5 100644 --- a/TODO +++ b/TODO @@ -3,3 +3,4 @@ - Blackhole zones - DNSSEC - Logging +- fix parse_name to know @ diff --git a/example/__init__.py b/example/__init__.py index 557ba09..8c6fd28 100644 --- a/example/__init__.py +++ b/example/__init__.py @@ -1,4 +1,5 @@ from nsconfig import Nsc +import nsconfig.sink from nsconfig.daemon.bind import NscDaemonBind nsc = Nsc( @@ -7,6 +8,9 @@ nsc = Nsc( daemon=NscDaemonBind(control_command='echo'), ) +nsconfig.sink.generate_localhost(nsc) +nsconfig.sink.generate_blackhole(nsc) + for rev in ['10.1.0.0/16', '10.2.0.0/16', 'fd12:3456:789a::/48']: rz = nsc.add_zone(reverse_for=rev) rz[""].NS('ns1.example.org', 'ns2.example.org') diff --git a/nsconfig/cli.py b/nsconfig/cli.py index 6e9fcdc..8367aa2 100644 --- a/nsconfig/cli.py +++ b/nsconfig/cli.py @@ -1,4 +1,5 @@ import argparse +from argparse import Namespace from pathlib import Path from texttable import Texttable @@ -36,10 +37,9 @@ def do_test(nsc: Nsc) -> None: nsc.daemon.dump_config(file=f) -def do_status(nsc: Nsc) -> None: +def do_status(nsc: Nsc, args: Namespace) -> None: table = Texttable(max_width=0) - table.header(['Zone', 'Old serial', 'Old hash', 'New serial', 'New hash', 'S']) - table.set_cols_dtype(['t', 'i', 't', 'i', 't', 't']) + table.header(['S', 'Zone', 'Type', 'Status']) table.set_deco(Texttable.HEADER) for z in nsc.get_zones(): @@ -47,21 +47,20 @@ def do_status(nsc: Nsc) -> None: action = '*' else: action = "" + do_show = args.all if isinstance(z, NscZonePrimary): - table.add_row([ - z.name, - z.prev_state.serial, - z.prev_state.hash, - z.state.serial, - z.state.hash, - action, - ]) + status = f'serial {z.prev_state.serial}' + if z.is_changed(): + status += f' -> {z.state.serial}' + do_show = True elif isinstance(z, NscZoneSecondary): - table.add_row([z.name, 'secondary', "", "", "", action]) + status = f'from {z.primary_server}' elif isinstance(z, NscZoneAlias): - table.add_row([z.name, 'alias', "", "", "", action]) + status = f'to {z.alias_for.name}' else: raise NotImplementedError() + if do_show: + table.add_row([action, z.name, z.zone_type.name, status]) print(table.draw()) @@ -88,6 +87,7 @@ def main(nsc: Nsc) -> None: test_parser = subparsers.add_parser('test', help='test new configuration', description='Test new configuration') status_parser = subparsers.add_parser('status', help='list status of zones', description='List status of zones') + status_parser.add_argument('-a', '--all', default=False, action='store_true', help='show non-primary zones') update_parser = subparsers.add_parser('update', help='update configuration', description='Update zone files and daemon configuration as needed') @@ -98,6 +98,6 @@ def main(nsc: Nsc) -> None: if args.action == 'test': do_test(nsc) elif args.action == 'status': - do_status(nsc) + do_status(nsc, args) elif args.action == 'update': do_update(nsc) diff --git a/nsconfig/core.py b/nsconfig/core.py index beb9655..885a460 100644 --- a/nsconfig/core.py +++ b/nsconfig/core.py @@ -64,7 +64,7 @@ class NscNode: else: self._add(dns.rdtypes.IN.AAAA.AAAA(RdataClass.IN, RdataType.AAAA, str(a))) if reverse: - self.nsc_zone.nsc._add_reverse_mapping(a, parse_name(self.name + '.' + self.nsc_zone.name)) + self.nsc_zone.nsc._add_reverse_mapping(a, parse_name(self.name + '.' + self.nsc_zone.name) if self.name != "" else parse_name(self.nsc_zone.name + '.')) return self def MX(self, pri: int, name: str) -> Self: diff --git a/nsconfig/sink.py b/nsconfig/sink.py new file mode 100644 index 0000000..8bd23cb --- /dev/null +++ b/nsconfig/sink.py @@ -0,0 +1,49 @@ +from typing import List, Optional + +from nsconfig.core import Nsc, NscZonePrimary +from nsconfig.util import IPNetwork, parse_network + +# Networks which should have blackhole reverse zones as recommended by RFC 6303 +BLACKHOLE_NETWORKS = [ + '0.0.0.0/8', # IPv4 reserved + '10.0.0.0/8', # IPv4 private + '169.254.0.0/16', # IPv4 link-local + '192.0.2.0/24', # IPv4 test + '192.168.0.0/16', # IPv4 private + '198.51.100.0/24', # IPv4 test + '203.0.113.0/24', # IPv4 test + '255.255.255.255/32', # IPv4 broadcast + '::0/128', # IPv6 unspecified + '2001:0db8::/32', # IPv6 example + 'fd00::/8', # IPv6 unique local + 'fe80::/12', # IPv6 link-local + 'fe90::/12', + 'fea0::/12', + 'feb0::/12', +] + [f'172.{i}.0.0/16' for i in range(16, 32)] # IPv4 private + + +def generate_localhost(nsc) -> None: + z = nsc.add_zone('localhost') + (z[""] + .NS(z.config.origin_server) + .A('127.0.0.1', '::1')) + + r4 = nsc.add_zone(reverse_for='127.0.0.0/8') + r4[""].NS(z.config.origin_server) + + r6 = nsc.add_zone(reverse_for='::1/128') + r6[""].NS(z.config.origin_server) + + +def generate_blackhole(nsc: Nsc, + use_zone: Optional[NscZonePrimary] = None, + skip_networks: List[IPNetwork] = [], + ) -> None: + if use_zone is None: + use_zone = nsc.add_zone('invalid') + assert isinstance(use_zone, NscZonePrimary) + for raw_net in BLACKHOLE_NETWORKS: + net = parse_network(raw_net) + if net not in skip_networks: + z = nsc.add_zone(reverse_for=net, alias_for=use_zone)