--- a/bin/pvl.dns-hosts Mon Dec 16 14:38:02 2013 +0200
+++ b/bin/pvl.dns-hosts Mon Dec 16 15:24:57 2013 +0200
@@ -9,6 +9,8 @@
import pvl.dhcp.config
import pvl.ldap.args
+import ipaddr
+import optparse
import collections
import re
import logging; log = logging.getLogger('main')
@@ -60,6 +62,9 @@
parser.add_option('--output-hosts', metavar='FILE',
help="Output hosts file")
+ parser.add_option('--output-ip', metavar='PREFIX',
+ help="Output hosts by ip prefix")
+
# defaults
parser.set_defaults(
@@ -73,7 +78,7 @@
return options, args
-def process_zone_hosts (options, file) :
+def import_zone_hosts (options, file) :
"""
Yield host info from zonefile records.
"""
@@ -99,7 +104,7 @@
else :
log.warn("%s: unknown rr: %s", rr.name, rr)
-def process_dhcp_host (options, host, items) :
+def import_dhcp_host (options, host, items) :
"""
Yield host infos from a dhcp host ... { ... }
"""
@@ -140,7 +145,7 @@
if hostname :
yield hostname, 'ethernet', ethernet
-def process_dhcp_hosts (options, blocks) :
+def import_dhcp_hosts (options, blocks) :
"""
Process hosts from a parsed block
"""
@@ -151,20 +156,20 @@
block, args = block[0], block[1:]
if block == 'group' :
- for info in process_dhcp_hosts(options, blocks) :
+ for info in import_dhcp_hosts(options, blocks) :
yield info
elif block == 'host' :
host, = args
try :
- for info in process_dhcp_host(options, host, items) :
+ for info in import_dhcp_host(options, host, items) :
yield info
except ValueError as error :
log.warn("%s: invalid host: %s", host, error)
else:
log.warn("ignore unknown block: %s", block)
-def process_dhcp_conf (options, file) :
+def import_dhcp_conf (options, file) :
items, blocks = pvl.dhcp.config.DHCPConfigParser().load(file)
for item in items :
@@ -172,29 +177,14 @@
if item == 'include' :
include, = args
- for info in process_dhcp_conf(options, pvl.args.apply_file(include)) :
+ for info in import_dhcp_conf(options, pvl.args.apply_file(include)) :
yield info
else :
log.warn("ignore unknown item: %s", item)
- for info in process_dhcp_hosts(options, blocks) :
+ for info in import_dhcp_hosts(options, blocks) :
yield info
-def apply_hosts_import (options) :
- """
- Import host infos from given files.
- """
-
- if options.import_zone_hosts:
- for info in process_zone_hosts(options,
- pvl.args.apply_file(options.import_zone_hosts)) :
- yield info
-
- if options.import_dhcp_hosts:
- for info in process_dhcp_conf(options,
- pvl.args.apply_file(options.import_dhcp_hosts)) :
- yield info
-
ZONE_COMMENTS = (
re.compile(r'(?P<owner>[^/]+)\s*-\s+(?P<host>.+)'),
re.compile(r'(?P<group>.+?)\s*/\s*(?P<owner>.+)\s+[/-]\s+(?P<host>.+)'),
@@ -239,26 +229,23 @@
if value :
yield field, value.strip()
-HOST_OWNERS = {
- u'tech': 'root',
- u'atk': 'root',
- u'toimisto': 'root',
-}
+NONE_OWNERS = set((
+ u'tech',
+ u'atk',
+ u'toimisto',
+))
-def process_host_owner (options, host, info) :
+def process_host_owner_ldap (options, host, info) :
"""
Yield guesses for user from LDAP.
"""
- if info.get('owner').lower() in HOST_OWNERS :
- yield HOST_OWNERS[info.get('owner').lower()]
-
if info.get('mail') :
for user in options.ldap.users.filter(
{ 'mailLocalAddress': info['mail'] },
{ 'uid': info['mail'] },
) :
- yield user['uid']
+ yield user, None
if info.get('group') and info.get('owner') :
groups = options.ldap.groups.filter(cn=info['group'])
@@ -268,13 +255,34 @@
'gidNumber': group['gidNumber'],
'cn': info['owner'],
}) :
- yield user['uid']
+ yield user, group
if info.get('owner') :
for user in options.ldap.users.filter({
'cn': info['owner'],
}) :
- yield user['uid']
+ yield user, None
+
+def process_host_owner (options, host, info) :
+ """
+ Return (owner, comment) for host based on info, or None.
+ """
+
+ if info.get('owner').lower() in NONE_OWNERS :
+ return
+
+ # from ldap?
+ for ldap in process_host_owner_ldap(options, host, info) :
+ user, group = ldap
+
+ if not group :
+ # get group from ldap
+ group = options.ldap.users.group(user)
+
+ return user['uid'], u"{group} / {user}".format(
+ user = user.getunicode('cn'),
+ group = group.getunicode('cn'),
+ )
def process_host_comments (options, host, info) :
"""
@@ -285,13 +293,15 @@
log.debug("%s: %s", host, info)
- for owner in process_host_owner(options, host, info) :
- log.info("%s: %s", host, owner)
+ owner = process_host_owner(options, host, info)
+ if owner :
+ owner, comment = owner
+
+ log.info("%s: %s (%s)", host, owner, comment)
+
+ yield 'owner-comment', comment
yield 'owner', owner,
-
- # only use the first match
- break
else :
log.warn("%s: no owner: %s", host, info)
@@ -323,12 +333,31 @@
for field, value in process_host_comments(options, host, fields) :
yield host, field, value
-
-def process_hosts_import (options, import_hosts) :
+
+def apply_hosts_import (options) :
"""
- Import host definitions from given infos
+ Import host infos from given files.
"""
+ if options.import_zone_hosts:
+ for info in import_zone_hosts(options,
+ pvl.args.apply_file(options.import_zone_hosts)) :
+ yield info
+
+ if options.import_dhcp_hosts:
+ for info in import_dhcp_conf(options,
+ pvl.args.apply_file(options.import_dhcp_hosts)) :
+ yield info
+
+def import_hosts (options) :
+ """
+ Import hosts from dns/dhcp.
+ """
+
+ import_hosts = apply_hosts_import(options)
+ import_hosts = process_hosts_comments(options, import_hosts)
+
+ # gather
hosts = collections.defaultdict(lambda: collections.defaultdict(list))
for host, field, value in import_hosts :
@@ -336,6 +365,50 @@
return hosts.iteritems()
+def select_hosts_ip (options, hosts, network) :
+ for host, fields in hosts :
+ ip = fields.get('ip')
+
+ if not ip :
+ continue
+
+ ip = ipaddr.IPAddress(ip[0])
+
+ if ip in network :
+ yield ip, host, fields
+
+
+def export_hosts (options, hosts) :
+ """
+ Export hosts to file.
+ """
+
+ file = pvl.args.apply_file(options.output_hosts, 'w', options.output_charset)
+
+ if options.output_ip :
+ prefix = ipaddr.IPNetwork(options.output_ip)
+
+ # filter + sort
+ hosts = [(host, fields) for ip, host, fields in sorted(select_hosts_ip(options, hosts, prefix))]
+
+ for host, fields in hosts :
+ for comment in fields.get('comment', ()) :
+ print >>file, u"# {comment}".format(comment=comment)
+
+ print >>file, u"[{host}]".format(host=host)
+
+ for field, fmt in (
+ ('ip', None),
+ ('ethernet', None),
+ ('owner', u"\t{field:15} = {value} # {fields[owner-comment][0]}"),
+ ) :
+ if not fmt :
+ fmt = u"\t{field:15} = {value}"
+
+ for value in fields.get(field, ()) :
+ print >>file, fmt.format(field=field, value=value, fields=fields)
+
+ print >>file
def main (argv) :
options, args = parse_options(argv)
@@ -347,20 +420,11 @@
hosts = pvl.args.apply_files(args, 'r', options.input_charset)
else :
# import
- import_hosts = apply_hosts_import(options)
- import_hosts = process_hosts_comments(options, import_hosts)
- hosts = process_hosts_import(options, import_hosts)
+ hosts = import_hosts(options)
# output
if options.output_hosts :
- for host, fields in hosts :
- print host
-
- for field, values in fields.iteritems() :
- for value in values :
- print "\t", field, "\t", value.encode(options.output_charset)
-
- return 0
+ export_hosts(options, hosts)
if __name__ == '__main__':
pvl.args.main(main)