|
1 #!/usr/bin/env python |
|
2 |
|
3 |
|
4 import pvl.args |
|
5 import pvl.dns.zone |
|
6 |
|
7 import configobj |
|
8 import ipaddr |
|
9 import logging; log = logging.getLogger('pvl.hosts-dns') |
|
10 import math |
|
11 import optparse |
|
12 |
|
13 def apply_hosts_file (options, file) : |
|
14 """ |
|
15 Load hosts from a file. |
|
16 """ |
|
17 config = configobj.ConfigObj(file, |
|
18 encoding = options.hosts_charset, |
|
19 ) |
|
20 |
|
21 for host in config.sections : |
|
22 yield host, config[host] |
|
23 |
|
24 def apply_hosts (options, args) : |
|
25 """ |
|
26 Load hosts from arguments. |
|
27 """ |
|
28 |
|
29 for file in pvl.args.apply_files(args, 'r') : |
|
30 for host in apply_hosts_file(options, file) : |
|
31 yield host |
|
32 |
|
33 def process_hosts_forward (options, hosts) : |
|
34 for host, fields in hosts : |
|
35 domain = fields.get('domain', options.hosts_domain) |
|
36 |
|
37 if options.forward_zone and domain != options.forward_zone : |
|
38 continue |
|
39 |
|
40 ip = fields.get('ip') |
|
41 |
|
42 if ip : |
|
43 yield pvl.dns.zone.ZoneRecord.A(host, value) |
|
44 |
|
45 # aliases |
|
46 for field, value in fields.iteritems() : |
|
47 if field.startswith('alias.') : |
|
48 yield pvl.dns.zone.ZoneRecord.CNAME(value, host) |
|
49 |
|
50 def apply_reverse_zone (options, prefix, address) : |
|
51 """ |
|
52 Determine the correct reverse-dns name for the given address with the reverse zone for the given prefix. |
|
53 """ |
|
54 |
|
55 assert prefix.version == address.version |
|
56 |
|
57 hostbits = prefix.max_prefixlen - prefix.prefixlen |
|
58 |
|
59 if prefix.version == 4 : |
|
60 labelbytes = int(math.ceil(hostbits / 8.0)) |
|
61 labelraw = address.packed[-labelbytes:] |
|
62 |
|
63 return '.'.join(reversed([str(ord(x)) for x in labelraw])) |
|
64 else : |
|
65 raise ValueError("unsupported address version: %s" % (prefix, )) |
|
66 |
|
67 def process_hosts_reverse (options, hosts) : |
|
68 for host, fields in hosts : |
|
69 domain = fields.get('domain', options.hosts_domain) |
|
70 ip = fields.get('ip') |
|
71 |
|
72 if ip : |
|
73 ip = ipaddr.IPAddress(ip) |
|
74 |
|
75 if not ip or ip not in options.reverse_zone : |
|
76 continue |
|
77 |
|
78 # reverse against the prefix origin |
|
79 ptr = apply_reverse_zone(options, options.reverse_zone, ip) |
|
80 |
|
81 yield pvl.dns.zone.ZoneRecord.PTR(ptr, pvl.dns.zone.fqdn(host, domain)) |
|
82 |
|
83 def main (argv) : |
|
84 """ |
|
85 Generate bind zonefiles from host definitions. |
|
86 """ |
|
87 |
|
88 parser = optparse.OptionParser(main.__doc__) |
|
89 parser.add_option_group(pvl.args.parser(parser)) |
|
90 |
|
91 hosts = optparse.OptionGroup(parser, "Hosts input") |
|
92 hosts.add_option('--hosts-charset', metavar='CHARSET', default='utf-8', |
|
93 help="Encoding used for host files") |
|
94 |
|
95 hosts.add_option('--hosts-domain', metavar='DOMAIN', |
|
96 help="Default domain for hosts") |
|
97 |
|
98 parser.add_option_group(hosts) |
|
99 |
|
100 parser.add_option('--forward-zone', metavar='DOMAIN', |
|
101 help="Generate forward zone for domain") |
|
102 |
|
103 parser.add_option('--reverse-zone', metavar='PREFIX', |
|
104 help="Generate reverse zone for prefx") |
|
105 |
|
106 options, args = parser.parse_args(argv[1:]) |
|
107 pvl.args.apply(options) |
|
108 |
|
109 # input |
|
110 hosts = list(apply_hosts(options, args)) |
|
111 |
|
112 |
|
113 if options.forward_zone : |
|
114 zone = process_hosts_forward(options, hosts) |
|
115 |
|
116 elif options.reverse_zone : |
|
117 options.reverse_zone = ipaddr.IPNetwork(options.reverse_zone) |
|
118 |
|
119 zone = process_hosts_reverse(options, hosts) |
|
120 |
|
121 else : |
|
122 log.error("nothing to do") |
|
123 return 1 |
|
124 |
|
125 for record in zone : |
|
126 print unicode(record) |
|
127 |
|
128 if __name__ == '__main__': |
|
129 pvl.args.main(main) |