pvl/hosts.py
author Tero Marttila <terom@paivola.fi>
Tue, 17 Dec 2013 01:15:37 +0200
changeset 299 df653511caf9
parent 290 9e626c1e935d
child 305 e85c95e757eb
permissions -rw-r--r--
pvl.hosts.Host.fqnd(): assume any host with a . is a fqdn
"""
    Host definitions.
"""

import pvl.args
import pvl.dns.zone

import configobj
import ipaddr
import optparse
import os.path

def optparser (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")
    
    return hosts

class Host (object) :
    @classmethod
    def expand (cls, options, host, range, ip, **opts) :
        host = pvl.dns.zone.parse_generate_field(host)
        ip = pvl.dns.zone.parse_generate_field(ip)

        for i in range :
            yield cls.build(options, host(i),
                    ip  = ip(i),
                    **opts
            )

    @classmethod
    def config (cls, options, host, ip=None, **extra) :
        """
            Yield Hosts from a config section's scalars.
        """

        if '{' in host :
            pre, host = host.split('{', 1)
            range, post = host.rsplit('}', 1)
            
            range = pvl.dns.zone.parse_generate_range(range)
            host = pre + "$" + post

            for host in cls.expand(options, host, range, ip, **extra) :
                yield host
        else :
            yield cls.build(options, host, ip=ip, **extra)
    
    @classmethod
    def build (cls, options, host, domain=None, ip=None, ip6=None, owner=None, boot=None, **extra) :
        """
            Return a Host from a config section's scalars.
        """

        ethernet = []
        alias = []

        for attr, value in extra.iteritems() :
            if attr.startswith('ethernet') :
                ethernet.append(value)
            elif attr.startswith('alias') :
                alias.append(value)
            else :
                raise ValueError("Unknown host attr: %s=%s" % (attr, value))
        
        if domain is None :
            domain = options.hosts_domain
        
        return cls(host,
                domain      = domain,
                ip          = ipaddr.IPv4Address(ip) if ip else None,
                ip6         = ipaddr.IPv6Address(ip6) if ip6 else None,
                ethernet    = ethernet,
                alias       = alias,
                owner       = owner,
                boot        = boot,
        )

    def __init__ (self, host, domain=None, ip=None, ip6=None, ethernet=(), alias=(), owner=None, boot=None) :
        """
            host        - str
            domain      - str
            ip          - ipaddr.IPv4Address
            ip6         - ipaddr.IPv6Address
            ethernet    - list
            alias       - list
            owner       - str: LDAP uid
        """
        self.host = host
        self.domain = domain
        self.ip = ip
        self.ip6 = ip6
        self.ethernet = ethernet
        self.alias = alias
        self.owner = owner
        self.boot = boot

    def fqdn (self) :
        if '.' in self.host :
            return self.host + '.'
        elif self.domain :
            return pvl.dns.zone.fqdn(self.host, self.domain)
        else :
            raise ValueError("%s: have no fqdn/domain" % (self, ))

    def __str__ (self) :
        return str(self.host)

def apply_hosts_config (options, config, name, defaults={}) :
    """
        Load hosts from a ConfigObj section.
    """

    scalars = dict((scalar, config[scalar]) for scalar in config.scalars)

    if config.sections :
        # recurse; this is a domain meta-section
        params = dict(defaults, domain=name, **scalars)

        for section in config.sections :
            for host in apply_hosts_config(options, config[section], section, params) :
                yield host

    elif name :
        params = dict(defaults, **scalars)

        # this is a host section
        for host in Host.config(options, name, **params) :
            yield host

    else :
        raise ValueError("No sections in config")


def apply_hosts_file (options, file) :
    """
        Load Hosts from a file.

    """

    config = configobj.ConfigObj(file,
            encoding    = options.hosts_charset,
    )
    
    # use file basename as default
    name = os.path.basename(file.name)
    
    return apply_hosts_config(options, config, name)

def apply_hosts (options, files) :
    """
        Load Hosts from files.
    """

    for file in files :
        for host in apply_hosts_file(options, file) :
            yield host

def sort_hosts (options, hosts) :
    """
        Yields hosts with a sorting key.
    """

    for host in hosts :
        if host.ip :
            sort = host.ip
        else :
            # sorts first
            sort = ipaddr.IPAddress(0)

        yield sort, host

def apply (options, args) :
    """
        Load Hosts from arguments.
    """
    
    # without unicode
    files = pvl.args.apply_files(args, 'r')

    # load configs
    hosts = apply_hosts(options, files)

    # sort
    hosts = list(sort_hosts(options, hosts))
    hosts.sort()
    hosts = [host for sort, host in hosts]
    
    return hosts