# HG changeset patch # User Tero Marttila # Date 1387200297 -7200 # Node ID 5ee3bcd5b4b58defde876d463a38b271fa22f76b # Parent 36c980a6d04adf49d6f0ade7bb5d2ad8e1717258 pvl.dns-hosts: name import/export; export as config diff -r 36c980a6d04a -r 5ee3bcd5b4b5 bin/pvl.dns-hosts --- 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[^/]+)\s*-\s+(?P.+)'), re.compile(r'(?P.+?)\s*/\s*(?P.+)\s+[/-]\s+(?P.+)'), @@ -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)