pvl.dns-hosts: name import/export; export as config
authorTero Marttila <terom@paivola.fi>
Mon, 16 Dec 2013 15:24:57 +0200
changeset 263 5ee3bcd5b4b5
parent 262 36c980a6d04a
child 264 ce29be46f705
pvl.dns-hosts: name import/export; export as config
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<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)