From ac0d54814a3bf915a8fe12ac598a93ad6ce63cb0 Mon Sep 17 00:00:00 2001 From: Thomas Oettli Date: Sun, 18 Jan 2026 16:22:29 +0100 Subject: [PATCH] Re-structure for packaging --- README.md | 1 + {etc => files}/config.yml | 0 {etc => files}/templates/zone.config.template | 0 {etc => files}/templates/zone.template | 0 pyproject.toml | 42 ++++++++++++++++ {dnsmgr => src/dnsmgr}/__init__.py | 4 +- dns-confgen => src/dnsmgr/confgen.py | 15 +++--- dns-list-zones => src/dnsmgr/list-zones.py | 14 +++--- dns-record-add => src/dnsmgr/record-add.py | 49 ++++++++++++------- .../dnsmgr/record-delete.py | 46 ++++++++++------- dns-zone-add => src/dnsmgr/zone-add.py | 46 ++++++++++------- dns-zone-delete => src/dnsmgr/zone-delete.py | 27 +++++----- dns-zone-list => src/dnsmgr/zone-list.py | 20 ++++---- 13 files changed, 166 insertions(+), 98 deletions(-) rename {etc => files}/config.yml (100%) rename {etc => files}/templates/zone.config.template (100%) rename {etc => files}/templates/zone.template (100%) create mode 100644 pyproject.toml rename {dnsmgr => src/dnsmgr}/__init__.py (99%) rename dns-confgen => src/dnsmgr/confgen.py (77%) mode change 100755 => 100644 rename dns-list-zones => src/dnsmgr/list-zones.py (90%) mode change 100755 => 100644 rename dns-record-add => src/dnsmgr/record-add.py (75%) mode change 100755 => 100644 rename dns-record-delete => src/dnsmgr/record-delete.py (80%) mode change 100755 => 100644 rename dns-zone-add => src/dnsmgr/zone-add.py (82%) mode change 100755 => 100644 rename dns-zone-delete => src/dnsmgr/zone-delete.py (86%) mode change 100755 => 100644 rename dns-zone-list => src/dnsmgr/zone-list.py (89%) mode change 100755 => 100644 diff --git a/README.md b/README.md index 46f81fa..bb8446b 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,4 @@ Manage DNS zones and dynamic records. * [Bind-Tools](https://www.isc.org/bind/) (if not bundled with Bind) * [dnspython](https://www.dnspython.org) * [prettytable](https://zetcode.com/python/prettytable/) +* [pyyaml](https://pyyaml.org) diff --git a/etc/config.yml b/files/config.yml similarity index 100% rename from etc/config.yml rename to files/config.yml diff --git a/etc/templates/zone.config.template b/files/templates/zone.config.template similarity index 100% rename from etc/templates/zone.config.template rename to files/templates/zone.config.template diff --git a/etc/templates/zone.template b/files/templates/zone.template similarity index 100% rename from etc/templates/zone.template rename to files/templates/zone.template diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3e99201 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[build-system] +requires = ["setuptools >= 77.0.3"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.dynamic] +version = {attr = "dnsmgr.__version__"} + +[project] +name = "dnsmgr" +dynamic = ["version"] +dependencies = [ + "dnspython>=2.8.0", + "prettytable>=3.17.0", + "pyyaml>=6.0.3" +] +requires-python = ">=3.11" +authors = [ + {name = "Thomas Oettli", email = "spacefreak@noop.ch"} +] +maintainers = [ + {name = "Thomas Oettli", email = "spacefreak@noop.ch"} +] +description = "Manage DNS zones and dynamic records." +readme = "README.md" +license = "GPL-3.0-only" +keywords = ["dns"] +classifiers = [ + "Development Status :: 4 - Beta", + "Topic :: Internet :: Name Service (DNS)", + "Intended Audience :: System Administrators", + "Programming Language :: Python :: 3" +] + +[project.scripts] +dns-confgen = "dnsmgr.confgen:main" +dns-list-zones = "dnsmgr.list-zones:main" +dns-record-add = "dnsmgr.record-add:main" +dns-record-delete = "dnsmgr.record-delete:main" +dns-zone-add = "dnsmgr.zone-add:main" +dns-zone-delete = "dnsmgr.zone-delete:main" +dns-zone-list = "dnsmgr.zone-list:main" + diff --git a/dnsmgr/__init__.py b/src/dnsmgr/__init__.py similarity index 99% rename from dnsmgr/__init__.py rename to src/dnsmgr/__init__.py index a329ffa..9e94868 100644 --- a/dnsmgr/__init__.py +++ b/src/dnsmgr/__init__.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import dns.name import dns.tsig import dns.rcode @@ -20,6 +18,8 @@ from hashlib import sha1 from prettytable import PrettyTable from shutil import chown +__version__ = "1.0.0" +__author__ = "Thomas Oettli " DEFAULT_CFGFILE = '/etc/dns-manager/config.yml' diff --git a/dns-confgen b/src/dnsmgr/confgen.py old mode 100755 new mode 100644 similarity index 77% rename from dns-confgen rename to src/dnsmgr/confgen.py index 3a938c9..dbe1ded --- a/dns-confgen +++ b/src/dnsmgr/confgen.py @@ -1,19 +1,18 @@ -#!/usr/bin/env python3 - import argparse -import dnsmgr import sys +from . import DEFAULT_CFGFILE, DNSManager, printe + def main(): parser = argparse.ArgumentParser(description='Generate Bind config files.') - parser.add_argument('-c', '--config', help='path to config file', default=dnsmgr.DEFAULT_CFGFILE) + parser.add_argument('-c', '--config', help='path to config file', default=DEFAULT_CFGFILE) args = parser.parse_args() try: - manager = dnsmgr.DNSManager(cfgfile=args.config) + manager = DNSManager(cfgfile=args.config) except RuntimeError as e: - dnsmgr.printe(f'config: {e}') + printe(f'config: {e}') sys.exit(100) try: @@ -25,7 +24,7 @@ def main(): print('OK') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(150) print('Reloading named... ', end='') @@ -34,7 +33,7 @@ def main(): manager.named_reload() print('OK') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(170) diff --git a/dns-list-zones b/src/dnsmgr/list-zones.py old mode 100755 new mode 100644 similarity index 90% rename from dns-list-zones rename to src/dnsmgr/list-zones.py index a9c78ec..7a9bd4c --- a/dns-list-zones +++ b/src/dnsmgr/list-zones.py @@ -1,9 +1,7 @@ -#!/usr/bin/env python3 - import argparse -import dnsmgr import sys +from . import DEFAULT_CFGFILE, DNSManager, printe, prettytable from json import dumps @@ -13,7 +11,7 @@ def main(): formatter_class=lambda prog: argparse.HelpFormatter( prog, max_help_position=45, width=140)) parser.add_argument('-a', '--all-zones', help='do not ignore zones that are not managed', action='store_true') - parser.add_argument('-c', '--config', help='path to config file', default=dnsmgr.DEFAULT_CFGFILE) + parser.add_argument('-c', '--config', help='path to config file', default=DEFAULT_CFGFILE) parser.add_argument('-d', '--decode', help='decode internationalized domain names (IDN)', action='store_true') output = parser.add_mutually_exclusive_group() output.add_argument('-j', '--json', help='print json format', action='store_true') @@ -22,15 +20,15 @@ def main(): args = parser.parse_args() try: - manager = dnsmgr.DNSManager(cfgfile=args.config) + manager = DNSManager(cfgfile=args.config) except RuntimeError as e: - dnsmgr.printe(f'config: {e}') + printe(f'config: {e}') sys.exit(100) try: zones = manager.all_zones if args.all_zones else manager.zones except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(150) zones.sort(key=lambda zone: zone.origin.to_unicode() if args.decode else zone.origin.to_text()) @@ -63,7 +61,7 @@ def main(): if args.all_zones: row.append(zone.cfgfile is not None) rows.append(row) - print(dnsmgr.prettytable(field_names, rows)) + print(prettytable(field_names, rows)) print(f'\nTotal: {len(rows)}\n') diff --git a/dns-record-add b/src/dnsmgr/record-add.py old mode 100755 new mode 100644 similarity index 75% rename from dns-record-add rename to src/dnsmgr/record-add.py index eb8ef42..58c75e0 --- a/dns-record-add +++ b/src/dnsmgr/record-add.py @@ -1,11 +1,24 @@ -#!/usr/bin/env python3 - import argparse import dns.rdataclass import dns.rdataset -import dnsmgr import sys +from . import ( + DEFAULT_CFGFILE, + NAMED_DEFAULT_VIEW, + DNSManager, + input_name, + input_rdata, + input_ttl, + input_yes_no, + name_from_text, + printe, + rdata_from_text, + select_type, + ttl_from_text, + type_from_text +) + def main(): preparser = argparse.ArgumentParser(add_help=False) @@ -18,7 +31,7 @@ def main(): parser.add_argument('-a', '--all-zones', help='allow zones that are not managed', action='store_true') parser.add_argument('-A', '--all-types', help='allow unsupported record types', action='store_true') parser.add_argument('-b', '--batch', help='run in batch mode (no user input)', action='store_true') - parser.add_argument('-c', '--config', help='path to config file', default=dnsmgr.DEFAULT_CFGFILE) + parser.add_argument('-c', '--config', help='path to config file', default=DEFAULT_CFGFILE) parser.add_argument('zone', metavar='ZONE[@VIEWS]', nargs=nargs, help='DNS zone name and optional list of views (comma separated or asterisk to select all views)', default=None) parser.add_argument('name', metavar='NAME', nargs=nargs, help='DNS record name', default=None) parser.add_argument('ttl', metavar='TTL', nargs=nargs, help='DNS record TTL in seconds', type=int, default=None) @@ -27,9 +40,9 @@ def main(): args = parser.parse_args() try: - manager = dnsmgr.DNSManager(cfgfile=args.config) + manager = DNSManager(cfgfile=args.config) except RuntimeError as e: - dnsmgr.printe(f'config: {e}') + printe(f'config: {e}') sys.exit(100) try: @@ -41,27 +54,27 @@ def main(): origin = zones[0].origin if args.name is None: - name = dnsmgr.input_name(origin, prompt='Record name') + name = input_name(origin, prompt='Record name') else: - name = dnsmgr.name_from_text(args.name, origin) + name = name_from_text(args.name, origin) if args.ttl is None: - ttl = dnsmgr.input_ttl() + ttl = input_ttl() else: - ttl = dnsmgr.ttl_from_text(args.ttl) + ttl = ttl_from_text(args.ttl) if args.type is None: - rdtype = dnsmgr.select_type(args.all_types) + rdtype = select_type(args.all_types) else: - rdtype = dnsmgr.type_from_text(args.type, args.all_types) + rdtype = type_from_text(args.type, args.all_types) if not args.value: - rdata = dnsmgr.input_rdata(rdtype, origin) + rdata = input_rdata(rdtype, origin) else: - rdata = dnsmgr.rdata_from_text(rdtype, ' '.join(args.value), origin) + rdata = rdata_from_text(rdtype, ' '.join(args.value), origin) except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(150) except KeyboardInterrupt: sys.exit(0) @@ -75,12 +88,12 @@ def main(): print(f'View: {zone.view}') print(f'\033[32m+ {name} {text}\033[0m\n') - if not dnsmgr.input_yes_no(): + if not input_yes_no(): sys.exit(0) for zone in zones: origin = zone.origin.to_text(omit_final_dot=True) - if len(zones) > 1 or zone.view != dnsmgr.NAMED_DEFAULT_VIEW: + if len(zones) > 1 or zone.view != NAMED_DEFAULT_VIEW: origin = f'{origin}@{zone.view}' print(f"Sending DDNS updates for '{origin}'... ", end='') @@ -88,7 +101,7 @@ def main(): manager.add_zone_record(zone, name, rdataset) print('OK') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(160) diff --git a/dns-record-delete b/src/dnsmgr/record-delete.py old mode 100755 new mode 100644 similarity index 80% rename from dns-record-delete rename to src/dnsmgr/record-delete.py index d13febf..854db8e --- a/dns-record-delete +++ b/src/dnsmgr/record-delete.py @@ -1,10 +1,20 @@ -#!/usr/bin/env python3 - import argparse import dns.rdataclass -import dnsmgr import sys +from . import ( + DEFAULT_CFGFILE, + NAMED_DEFAULT_VIEW, + RECORD_TYPES, + DNSManager, + input_yes_no, + name_from_text, + prettyselect, + printe, + rdata_from_text, + type_from_text, +) + def main(): preparser = argparse.ArgumentParser(add_help=False) @@ -16,7 +26,7 @@ def main(): parser.add_argument('-a', '--all-zones', help='allow zones that are not managed', action='store_true') parser.add_argument('-A', '--all-types', help='allow unsupported record types', action='store_true') parser.add_argument('-b', '--batch', help='run in batch mode (no user input)', action='store_true') - parser.add_argument('-c', '--config', help='path to config file', default=dnsmgr.DEFAULT_CFGFILE) + parser.add_argument('-c', '--config', help='path to config file', default=DEFAULT_CFGFILE) parser.add_argument('zone', metavar='ZONE[@VIEWS]', nargs=nargs, help='DNS zone name and optional list of views (comma separated or asterisk to select all views)', default=None) parser.add_argument('name', metavar='NAME', nargs=nargs, help='DNS record name', default=None) parser.add_argument('type', metavar='TYPE', nargs=nargs, help='DNS record type', default=None) @@ -24,9 +34,9 @@ def main(): args = parser.parse_args() try: - manager = dnsmgr.DNSManager(cfgfile=args.config) + manager = DNSManager(cfgfile=args.config) except RuntimeError as e: - dnsmgr.printe(f'config: {e}') + printe(f'config: {e}') sys.exit(100) try: @@ -43,10 +53,10 @@ def main(): if args.name is None: names = sorted(set([name.to_unicode() for name in zone for zone in zones])) rows = [[name] for name in names] - index = dnsmgr.prettyselect(['Record name'], rows, prompt='Select record name') + index = prettyselect(['Record name'], rows, prompt='Select record name') args.name = names[index] - name = dnsmgr.name_from_text(args.name, origin) + name = name_from_text(args.name, origin) for zone in zones: zone.filter_by_name(name, origin) @@ -58,13 +68,13 @@ def main(): if args.type is None: rdtypes = sorted(set([rdataset.rdtype for rdataset in zone.get_node(name) for zone in zones])) if not args.all_types: - rdtypes = list(filter(lambda rdtype: rdtype in dnsmgr.RECORD_TYPES, rdtypes)) + rdtypes = list(filter(lambda rdtype: rdtype in RECORD_TYPES, rdtypes)) rdtypes = [rdtype.to_text(rdtype) for rdtype in rdtypes] rows = [[rdtype] for rdtype in rdtypes] - index = dnsmgr.prettyselect(['Record type'], rows, prompt='Select record type') + index = prettyselect(['Record type'], rows, prompt='Select record type') args.type = rdtypes[index] - rdtype = dnsmgr.type_from_text(args.type, args.all_types) + rdtype = type_from_text(args.type, args.all_types) for zone in zones: zone.filter_by_rdtype(rdtype) @@ -74,7 +84,7 @@ def main(): raise RuntimeError(f"No such {rdtype.to_text(rdtype)} record -- '{name.to_text(True)}'") rdata = None - if not args.value and not args.batch and not dnsmgr.input_yes_no(f'Delete all {rdtype.to_text(rdtype)}-records?'): + if not args.value and not args.batch and not input_yes_no(f'Delete all {rdtype.to_text(rdtype)}-records?'): values = [] for zone in zones: for rdataset in zone.get_node(name): @@ -82,12 +92,12 @@ def main(): values.append(rdata.to_text(origin=zone.origin, relativize=False)) values = sorted(set(values)) rows = [[value] for value in values] - index = dnsmgr.prettyselect(['Record value'], rows, prompt='Select record value', truncate=True) + index = prettyselect(['Record value'], rows, prompt='Select record value', truncate=True) args.value = [values[index]] if args.value: value = ' '.join(args.value) - rdata = dnsmgr.rdata_from_text(rdtype, value, origin) + rdata = rdata_from_text(rdtype, value, origin) for zone in zones: zone.filter_by_rdata(rdata) @@ -96,7 +106,7 @@ def main(): raise RuntimeError(f"No such DNS record found -- {name.to_text(True)} IN {rdtype.to_text(rdtype)} {value}") except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(150) except KeyboardInterrupt: sys.exit(0) @@ -115,12 +125,12 @@ def main(): print(f'\033[31m- {name} {rdataset.ttl} {rdclassstr} {rdtypestr} {text}\033[0m') print() - if not dnsmgr.input_yes_no(): + if not input_yes_no(): sys.exit(0) for zone in zones: origin = zone.origin.to_text(omit_final_dot=True) - if len(zones) > 1 or zone.view != dnsmgr.NAMED_DEFAULT_VIEW: + if len(zones) > 1 or zone.view != NAMED_DEFAULT_VIEW: origin = f'{origin}@{zone.view}' print(f"Sending DDNS updates for '{origin}'... ", end='') @@ -131,7 +141,7 @@ def main(): manager.delete_zone_record(zone, name, rdataset) print('OK') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(160) diff --git a/dns-zone-add b/src/dnsmgr/zone-add.py old mode 100755 new mode 100644 similarity index 82% rename from dns-zone-add rename to src/dnsmgr/zone-add.py index 3b789ad..3207fcc --- a/dns-zone-add +++ b/src/dnsmgr/zone-add.py @@ -1,12 +1,20 @@ -#!/usr/bin/env python3 - import argparse import dns.rdataclass import dns.rdataset import dns.rdatatype -import dnsmgr import sys +from . import ( + DEFAULT_CFGFILE, + NAMED_DEFAULT_VIEW, + DNSManager, + input_name, + input_yes_no, + name_views_from_text, + prettyselect, + printe, + rdata_from_text, +) from time import sleep @@ -18,32 +26,32 @@ def main(): parser = argparse.ArgumentParser(description='Add DNS zones.') parser.add_argument('-b', '--batch', help='run in batch mode (no user input)', action='store_true') - parser.add_argument('-c', '--config', help='path to config file', default=dnsmgr.DEFAULT_CFGFILE) + parser.add_argument('-c', '--config', help='path to config file', default=DEFAULT_CFGFILE) parser.add_argument('-t', '--config-template', help='config file/template (overrides value set in ZONE_TEMPLATES config option)', default=None) parser.add_argument('-z', '--zone-template', help='zone file/template (overrides value set in ZONE_TEMPLATES config option)', default=None) parser.add_argument('zone', metavar='ZONE[@VIEWS]', nargs=nargs, help='DNS zone name and optional list of views (comma separated or asterisk to select all views)', default=None) args = parser.parse_args() try: - manager = dnsmgr.DNSManager(cfgfile=args.config) + manager = DNSManager(cfgfile=args.config) except RuntimeError as e: - dnsmgr.printe(f'config: {e}') + printe(f'config: {e}') sys.exit(100) managed_views = sorted(manager.config.zones_config.keys()) try: if args.zone is None: - name = dnsmgr.input_name() + name = input_name() rows = [[view] for view in managed_views] - index = dnsmgr.prettyselect(['View'], rows, prompt='Select view', also_valid=['*']) + index = prettyselect(['View'], rows, prompt='Select view', also_valid=['*']) views = managed_views if index == '*' else [managed_views[index]] else: - (name, views) = dnsmgr.name_views_from_text(args.zone) + (name, views) = name_views_from_text(args.zone) if views is None: if len(managed_views) > 1: raise RuntimeError('multiple managed views configured but none specified') - elif managed_views[0] != dnsmgr.NAMED_DEFAULT_VIEW: + elif managed_views[0] != NAMED_DEFAULT_VIEW: raise RuntimeError('the default view is not managed') views = managed_views elif views == '*': @@ -59,7 +67,7 @@ def main(): raise RuntimeError(f'zone already exists in view {views}') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(150) except KeyboardInterrupt: sys.exit(0) @@ -70,13 +78,13 @@ def main(): print(f'View: {view}') print(f'\033[32m+ {origin}\033[0m\n') - if not dnsmgr.input_yes_no(): + if not input_yes_no(): sys.exit(0) zones = [] for view in views: origin = name.to_text(omit_final_dot=True) - if len(views) > 1 or view != dnsmgr.NAMED_DEFAULT_VIEW: + if len(views) > 1 or view != NAMED_DEFAULT_VIEW: origin = f'{origin}@{view}' print(f"Adding zone '{origin}'... ", end='') @@ -87,7 +95,7 @@ def main(): if manager.config.zones_config[view].catalog_zone: zones.append(zone) except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(160) try: @@ -95,7 +103,7 @@ def main(): manager.named_reload() print('OK') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(170) if zones: @@ -109,16 +117,16 @@ def main(): raise RuntimeError(f'catalog zone of view \'{zone.view}\': {e}') origin = zone.origin.to_text(omit_final_dot=True) - if len(zones) > 1 or zone.view != dnsmgr.NAMED_DEFAULT_VIEW: + if len(zones) > 1 or zone.view != NAMED_DEFAULT_VIEW: origin = f'{origin}@{zone.view}' for catalog_zone in catalog_zones: - rdata = dnsmgr.rdata_from_text(dns.rdatatype.PTR, zone.origin.to_text(), catalog_zone.origin) + rdata = rdata_from_text(dns.rdatatype.PTR, zone.origin.to_text(), catalog_zone.origin) rdataset = dns.rdataset.Rdataset(dns.rdataclass.IN, dns.rdatatype.PTR, ttl=3600) rdataset.add(rdata) rdname = dns.name.from_text(zone.nfz() + '.zones', catalog_zone.origin) catalog_zone_origin = catalog_zone.origin.to_text(omit_final_dot=True) - if catalog_zone.view != dnsmgr.NAMED_DEFAULT_VIEW: + if catalog_zone.view != NAMED_DEFAULT_VIEW: catalog_zone_origin += f'@{catalog_zone.view}' try: @@ -126,7 +134,7 @@ def main(): manager.add_zone_record(catalog_zone, rdname, rdataset) print('OK') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) if __name__ == '__main__': diff --git a/dns-zone-delete b/src/dnsmgr/zone-delete.py old mode 100755 new mode 100644 similarity index 86% rename from dns-zone-delete rename to src/dnsmgr/zone-delete.py index 0a55d46..9648cbd --- a/dns-zone-delete +++ b/src/dnsmgr/zone-delete.py @@ -1,12 +1,11 @@ -#!/usr/bin/env python3 - import argparse import dns.rdataclass import dns.rdataset import dns.rdatatype -import dnsmgr import sys +from . import DEFAULT_CFGFILE, NAMED_DEFAULT_VIEW, DNSManager, printe, input_yes_no + def main(): preparser = argparse.ArgumentParser(add_help=False) @@ -16,14 +15,14 @@ def main(): parser = argparse.ArgumentParser(description='Delete DNS zones.') parser.add_argument('-b', '--batch', help='run in batch mode (no user input)', action='store_true') - parser.add_argument('-c', '--config', help='path to config file', default=dnsmgr.DEFAULT_CFGFILE) + parser.add_argument('-c', '--config', help='path to config file', default=DEFAULT_CFGFILE) parser.add_argument('zone', metavar='ZONE[@VIEWS]', nargs=nargs, help='DNS zone name and optional list of views (comma separated or asterisk to select all views)', default=None) args = parser.parse_args() try: - manager = dnsmgr.DNSManager(cfgfile=args.config) + manager = DNSManager(cfgfile=args.config) except RuntimeError as e: - dnsmgr.printe(f'config: {e}') + printe(f'config: {e}') sys.exit(100) try: @@ -32,7 +31,7 @@ def main(): else: zones = manager.get_zones(args.zone) except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(150) except KeyboardInterrupt: sys.exit(0) @@ -43,12 +42,12 @@ def main(): print(f'View: {zone.view}') print(f'\033[31m- {origin}\033[0m\n') - if not dnsmgr.input_yes_no(): + if not input_yes_no(): sys.exit(0) for zone in zones: origin = zone.origin.to_text(omit_final_dot=True) - if len(zones) > 1 or zone.view != dnsmgr.NAMED_DEFAULT_VIEW: + if len(zones) > 1 or zone.view != NAMED_DEFAULT_VIEW: origin = f'{origin}@{zone.view}' try: @@ -72,7 +71,7 @@ def main(): continue catalog_zone_origin = catalog_zone.origin.to_text(omit_final_dot=True) - if catalog_zone.view != dnsmgr.NAMED_DEFAULT_VIEW: + if catalog_zone.view != NAMED_DEFAULT_VIEW: catalog_zone_origin += f'@{catalog_zone.view}' print(f'Removing zone \'{origin}\' from catalog zone \'{catalog_zone_origin}\'... ', end='') manager.delete_zone_record(catalog_zone, rdname, rdataset) @@ -82,7 +81,7 @@ def main(): manager.delete_zone(zone) print('OK') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(160) try: @@ -90,12 +89,12 @@ def main(): manager.named_reload() print('OK') except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(170) for zone in zones: origin = zone.origin.to_text(omit_final_dot=True) - if len(zones) > 1 or zone.view != dnsmgr.NAMED_DEFAULT_VIEW: + if len(zones) > 1 or zone.view != NAMED_DEFAULT_VIEW: origin = f'{origin}@{zone.view}' print(f"Cleanup zone files of zone '{origin}'... ", end='') @@ -103,7 +102,7 @@ def main(): manager.cleanup_zone(zone) print('OK') except Exception as e: - dnsmgr.printe(e) + printe(e) sys.exit(180) diff --git a/dns-zone-list b/src/dnsmgr/zone-list.py old mode 100755 new mode 100644 similarity index 89% rename from dns-zone-list rename to src/dnsmgr/zone-list.py index de8f269..467f66b --- a/dns-zone-list +++ b/src/dnsmgr/zone-list.py @@ -1,10 +1,8 @@ -#!/usr/bin/env python3 - import argparse -import dnsmgr import re import sys +from . import NAMED_DEFAULT_VIEW, RECORD_TYPES, DEFAULT_CFGFILE, DNSManager, printe, prettytable from dns.reversename import ipv4_reverse_domain, ipv6_reverse_domain from json import dumps @@ -19,7 +17,7 @@ def main(): parser.add_argument('-a', '--all-zones', help='do not ignore zones that are not managed', action='store_true') parser.add_argument('-A', '--all-records', help='do not ignore unsupported record types', action='store_true') parser.add_argument('-b', '--batch', help='run in batch mode (no user input)', action='store_true') - parser.add_argument('-c', '--config', help='path to config file', default=dnsmgr.DEFAULT_CFGFILE) + parser.add_argument('-c', '--config', help='path to config file', default=DEFAULT_CFGFILE) parser.add_argument('-d', '--decode', help='decode internationalized domain names (IDN)', action='store_true') parser.add_argument('zone', metavar='ZONE[@VIEWS]', nargs=nargs, help='DNS zone name and optional list of views (comma separated or asterisk to select all views)', default=None) output = parser.add_mutually_exclusive_group() @@ -29,9 +27,9 @@ def main(): args = parser.parse_args() try: - manager = dnsmgr.DNSManager(cfgfile=args.config) + manager = DNSManager(cfgfile=args.config) except RuntimeError as e: - dnsmgr.printe(f'config: {e}') + printe(f'config: {e}') sys.exit(100) try: @@ -41,7 +39,7 @@ def main(): zones = manager.get_zones(args.zone, args.all_zones) except RuntimeError as e: - dnsmgr.printe(e) + printe(e) sys.exit(150) except KeyboardInterrupt: sys.exit(0) @@ -53,13 +51,13 @@ def main(): try: manager.get_zone_content(zone) except RuntimeError as e: - dnsmgr.printe(f"zone transfer of '{zone.origin.to_text(True)}@{zone.view}': {e}") + printe(f"zone transfer of '{zone.origin.to_text(True)}@{zone.view}': {e}") sys.exit(160) records = [] for name, node in zone.items(): for rdataset in node: - if not args.all_records and rdataset.rdtype not in dnsmgr.RECORD_TYPES: + if not args.all_records and rdataset.rdtype not in RECORD_TYPES: continue for value in rdataset: records.append({ @@ -101,9 +99,9 @@ def main(): row = [record['name'], record['ttl'], record['type'], record['value']] rows.append(row) - if len(views) > 1 or view != dnsmgr.NAMED_DEFAULT_VIEW: + if len(views) > 1 or view != NAMED_DEFAULT_VIEW: print(f'View: {view}') - print(dnsmgr.prettytable(field_names, rows, truncate=True)) + print(prettytable(field_names, rows, truncate=True)) print()