import argparse import dns.rdataclass 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) preparser.add_argument('-b', '--batch', action='store_true') preargs, args = preparser.parse_known_args() nargs = None if preargs.batch else '?' parser = argparse.ArgumentParser(description='Delete DNS records.') 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=DEFAULT_CFGFILE) parser.add_argument('-n', '--no-hooks', action='store_true', help='skip hook execution') 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, @ refers to zone apex', default=None) parser.add_argument('type', metavar='TYPE', nargs=nargs, help='DNS record type', default=None) parser.add_argument('value', metavar='VALUE', nargs='*', help='DNS record value, multiple values are choined by a space character', default=None) args = parser.parse_args() try: manager = DNSManager(cfgfile=args.config) except RuntimeError as e: printe(f'config: {e}') sys.exit(100) try: if args.zone is None: zones = manager.select_zones(args.all_zones) else: zones = manager.get_zones(args.zone, args.all_zones) for zone in zones: manager.get_zone_content(zone) origin = zones[0].origin 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 = prettyselect(['Record name'], rows, prompt='Select record name') args.name = names[index] name = name_from_text(args.name, origin) for zone in zones: zone.filter_by_name(name, origin) zones = list(filter(lambda zone: zone.nodes, zones)) if not zones: raise RuntimeError(f"No such DNS record -- '{name.to_text(True)}'") 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 RECORD_TYPES, rdtypes)) rdtypes = [rdtype.to_text(rdtype) for rdtype in rdtypes] rows = [[rdtype] for rdtype in rdtypes] index = prettyselect(['Record type'], rows, prompt='Select record type') args.type = rdtypes[index] rdtype = type_from_text(args.type, args.all_types) for zone in zones: zone.filter_by_rdtype(rdtype) zones = list(filter(lambda zone: zone.nodes, zones)) if not zones: 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 input_yes_no(f'Delete all {rdtype.to_text(rdtype)}-records?'): values = [] for zone in zones: for rdataset in zone.get_node(name): for rdata in rdataset: values.append(rdata.to_text(origin=zone.origin, relativize=False)) values = sorted(set(values)) rows = [[value] for value in values] index = prettyselect(['Record value'], rows, prompt='Select record value', truncate=True) args.value = [values[index]] if args.value: value = ' '.join(args.value) rdata = rdata_from_text(rdtype, value, origin) for zone in zones: zone.filter_by_rdata(rdata) zones = list(filter(lambda zone: zone.nodes, zones)) if not zones: raise RuntimeError(f"No such DNS record found -- {name.to_text(True)} IN {rdtype.to_text(rdtype)} {value}") except RuntimeError as e: printe(e) sys.exit(150) except KeyboardInterrupt: sys.exit(0) zones.sort(key=lambda zone: zone.view) if not args.batch: for zone in zones: print(f'View: {zone.view}') node = zone.find_node(name) rdataset = node.find_rdataset(dns.rdataclass.IN, rdtype) rdclassstr = rdataset.rdclass.to_text(rdataset.rdclass) rdtypestr = rdataset.rdtype.to_text(rdataset.rdtype) for rdata in rdataset: text = rdata.to_text(origin=zone.origin, relativize=False) print(f'\033[31m- {name} {rdataset.ttl} {rdclassstr} {rdtypestr} {text}\033[0m') print() if not input_yes_no(): sys.exit(0) for zone in zones: node = zone.find_node(name) rdataset = node.find_rdataset(dns.rdataclass.IN, rdtype) if not args.no_hooks: try: manager.run_record_hooks('delete', 'pre', zone, name, rdataset, args.batch) except RuntimeError as e: printe(e) sys.exit(190) origin = zone.origin.to_text(omit_final_dot=True) if len(zones) > 1 or zone.view != NAMED_DEFAULT_VIEW: origin = f'{origin}@{zone.view}' print(f"Sending DDNS updates for '{origin}'... ", end='') try: manager.delete_zone_record(zone, name, rdataset) print('OK') except RuntimeError as e: printe(e) sys.exit(160) if not args.no_hooks: try: manager.run_record_hooks('delete', 'post', zone, name, rdataset, args.batch) except RuntimeError as e: printe(e) if __name__ == '__main__': main()