--- a/bin/pvl.hosts-dns Mon Dec 16 18:37:08 2013 +0200
+++ b/bin/pvl.hosts-dns Mon Dec 16 19:00:58 2013 +0200
@@ -1,84 +1,53 @@
#!/usr/bin/env python
-
import pvl.args
+import pvl.hosts
import pvl.dns.zone
-import configobj
import ipaddr
import logging; log = logging.getLogger('pvl.hosts-dns')
-import math
import optparse
-def apply_hosts_file (options, file) :
- """
- Load hosts from a file.
+def process_hosts_forward (options, hosts, domain) :
"""
- config = configobj.ConfigObj(file,
- encoding = options.hosts_charset,
- )
-
- for host in config.sections :
- yield host, config[host]
-
-def apply_hosts (options, args) :
- """
- Load hosts from arguments.
+ Generate DNS ZoneRecords for the given domain's zone for hosts.
"""
- for file in pvl.args.apply_files(args, 'r') :
- for host in apply_hosts_file(options, file) :
- yield host
-
-def process_hosts_forward (options, hosts) :
- for host, fields in hosts :
- domain = fields.get('domain', options.hosts_domain)
-
- if options.forward_zone and domain != options.forward_zone :
+ for host in hosts :
+ if options.forward_zone and host.domain != domain :
continue
- ip = fields.get('ip')
-
- if ip :
- yield pvl.dns.zone.ZoneRecord.A(host, value)
+ if host.ip :
+ yield pvl.dns.zone.ZoneRecord.A(host, host.ip)
- # aliases
- for field, value in fields.iteritems() :
- if field.startswith('alias.') :
- yield pvl.dns.zone.ZoneRecord.CNAME(value, host)
+ for alias in host.alias :
+ yield pvl.dns.zone.ZoneRecord.CNAME(alias, host)
-def apply_reverse_zone (options, prefix, address) :
+def process_hosts_reverse (options, hosts, prefix) :
"""
- Determine the correct reverse-dns name for the given address with the reverse zone for the given prefix.
+ Generate DNS ZoneRecords within the given prefix's reverse-dns zone for hosts.
"""
- assert prefix.version == address.version
-
- hostbits = prefix.max_prefixlen - prefix.prefixlen
-
- if prefix.version == 4 :
- labelbytes = int(math.ceil(hostbits / 8.0))
- labelraw = address.packed[-labelbytes:]
+ for host in hosts :
+ if not host.ip :
+ continue
- return '.'.join(reversed([str(ord(x)) for x in labelraw]))
- else :
- raise ValueError("unsupported address version: %s" % (prefix, ))
-
-def process_hosts_reverse (options, hosts) :
- for host, fields in hosts :
- domain = fields.get('domain', options.hosts_domain)
- ip = fields.get('ip')
-
- if ip :
- ip = ipaddr.IPAddress(ip)
-
- if not ip or ip not in options.reverse_zone :
+ if host.ip not in prefix :
continue
- # reverse against the prefix origin
- ptr = apply_reverse_zone(options, options.reverse_zone, ip)
-
- yield pvl.dns.zone.ZoneRecord.PTR(ptr, pvl.dns.zone.fqdn(host, domain))
+ # reverse against the reverse-dns zone origin
+ yield pvl.dns.zone.ZoneRecord.PTR(
+ pvl.dns.zone.reverse_label(prefix, host.ip),
+ pvl.dns.zone.fqdn(host, host.domain)
+ )
+
+def apply_zone (options, zone) :
+ """
+ Output given ZoneRecord's
+ """
+
+ for record in zone :
+ print unicode(record)
def main (argv) :
"""
@@ -87,15 +56,7 @@
parser = optparse.OptionParser(main.__doc__)
parser.add_option_group(pvl.args.parser(parser))
-
- hosts = optparse.OptionGroup(parser, "Hosts input")
- hosts.add_option('--hosts-charset', metavar='CHARSET', default='utf-8',
- help="Encoding used for host files")
-
- hosts.add_option('--hosts-domain', metavar='DOMAIN',
- help="Default domain for hosts")
-
- parser.add_option_group(hosts)
+ parser.add_option_group(pvl.hosts.optparser(parser))
parser.add_option('--forward-zone', metavar='DOMAIN',
help="Generate forward zone for domain")
@@ -107,23 +68,18 @@
pvl.args.apply(options)
# input
- hosts = list(apply_hosts(options, args))
+ hosts = pvl.hosts.apply(options, args)
-
+ # process
if options.forward_zone :
- zone = process_hosts_forward(options, hosts)
+ apply_zone(options,
+ process_hosts_forward(options, hosts, options.forward_zone),
+ )
- elif options.reverse_zone :
- options.reverse_zone = ipaddr.IPNetwork(options.reverse_zone)
-
- zone = process_hosts_reverse(options, hosts)
-
- else :
- log.error("nothing to do")
- return 1
-
- for record in zone :
- print unicode(record)
+ if options.reverse_zone :
+ apply_zone(options,
+ process_hosts_reverse(options, hosts, ipaddr.IPNetwork(options.reverse_zone)),
+ )
if __name__ == '__main__':
pvl.args.main(main)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/dns/hosts.py Mon Dec 16 19:00:58 2013 +0200
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+"""
+ Process network hosts.
+"""
+
+class Hosts (object) :
+ def __init__ (self) :
+
--- a/pvl/dns/zone.py Mon Dec 16 18:37:08 2013 +0200
+++ b/pvl/dns/zone.py Mon Dec 16 19:00:58 2013 +0200
@@ -7,6 +7,7 @@
import codecs
import datetime
import logging
+import math
import os.path
log = logging.getLogger('pvl.dns.zone')
@@ -273,26 +274,26 @@
@classmethod
def A (cls, name, ip, **opts) :
- return cls(name, 'A', [ip], **opts)
+ return cls(str(name), 'A', [str(ip)], **opts)
@classmethod
def CNAME (cls, name, host, **opts) :
- return cls(name, 'CNAME', [host], **opts)
+ return cls(str(name), 'CNAME', [str(host)], **opts)
@classmethod
def TXT (cls, name, text, **opts) :
- return cls(name, 'TXT',
+ return cls(str(name), 'TXT',
[u'"{0}"'.format(text.replace('"', '\\"'))],
**opts
)
@classmethod
def PTR (cls, name, ptr, **opts) :
- return cls(name, 'PTR', [ptr], **opts)
+ return cls(str(name), 'PTR', [str(ptr)], **opts)
@classmethod
def MX (cls, name, priority, mx, **opts) :
- return cls(name, 'MX', [priority, mx], **opts)
+ return cls(str(name), 'MX', [int(priority), str(mx)], **opts)
def __init__ (self, name, type, data, origin=None, ttl=None, cls=None, line=None, comment=None) :
self.name = name
@@ -537,7 +538,7 @@
return '.'.join(tuple(reversed(parts)) + ( 'ip6', 'arpa'))
def fqdn (*parts) :
- fqdn = '.'.join(parts)
+ fqdn = '.'.join(str(part) for part in parts)
# we may be given an fqdn in parts
if not fqdn.endswith('.') :
@@ -545,4 +546,23 @@
return fqdn
+def reverse_label (prefix, address) :
+ """
+ Determine the correct label for the given IP address within the reverse zone for the given prefix.
+ This includes all suffix octets (partially) covered by the prefix.
+ """
+
+ assert prefix.version == address.version
+
+ hostbits = prefix.max_prefixlen - prefix.prefixlen
+
+ if prefix.version == 4 :
+ labelbytes = int(math.ceil(hostbits / 8.0))
+ labelraw = address.packed[-labelbytes:]
+
+ return '.'.join(reversed([str(ord(x)) for x in labelraw]))
+ else :
+ raise ValueError("unsupported address version: %s" % (prefix, ))
+
+