"""
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) :
# the label used for alias4/6 hosts
ALIAS4_FMT = '{host}-ipv4'
ALIAS6_FMT = '{host}-ipv6'
@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, alias=None, alias4=None, alias6=None, **extra) :
"""
Return a Host from a config section's scalars.
"""
if alias :
alias = alias.split()
else :
alias = ()
if alias4 :
alias4 = alias4.split()
else :
alias4 = ()
if alias6 :
alias6 = alias6.split()
else :
alias6 = ()
ethernet = { }
for field, value in extra.iteritems() :
if '.' in field :
field, instance = field.split('.')
else :
instance = None
if field == 'ethernet' :
if instance :
ethernet[instance] = value
else :
for eth in value.split() :
ethernet[len(ethernet)] = eth
else :
raise ValueError("%s: Unknown host field: %s=%s" % (host, field, 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,
alias4 = alias4,
alias6 = alias6,
owner = owner,
boot = boot,
)
def __init__ (self, host, domain=None, ip=None, ip6=None, ethernet={ }, alias=(), owner=None, boot=None, alias4=None, alias6=None) :
"""
host - str
domain - str
ip - ipaddr.IPv4Address
ip6 - ipaddr.IPv6Address
ethernet - { index: ethernet }
alias - list
owner - str: LDAP uid
alias4 - list (CNAME -> A)
alias6 - list (CNAME -> AAAA)
"""
self.host = host
self.domain = domain
self.ip = ip
self.ip6 = ip6
self.ethernet = ethernet
self.alias = alias
self.alias4 = alias4
self.alias6 = alias6
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)
params.update(**scalars) # override
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