pvl.hosts: improve HostExtension support enough to move boot= into pvl.hosts.dhcp
import logging
log = logging.getLogger('pvl.dns.generate')
class OffsetValue (object) :
"""
Magic for $GENERATE offsets.
>>> OffsetValue(0)[1]
1
>>> OffsetValue(10)[5]
15
"""
def __init__ (self, base) :
self.base = base
def __getitem__ (self, offset) :
return self.base + offset
def parse_generate_field (field) :
"""
Parse a $GENERATE lhs/rhs field:
$
${<offset>[,<width>[,<base>]]}
\$
$$
Returns a wrapper that builds the field-value when called with the index.
>>> parse_generate_field("foo")(1)
'foo'
>>> parse_generate_field("foo-$")(1)
'foo-1'
>>> parse_generate_field("foo-$$")(1)
'foo-$'
>>> parse_generate_field("\$")(1)
'$'
>>> parse_generate_field("10.0.0.${100}")(1)
'10.0.0.101'
>>> parse_generate_field("foo-${0,2,d}")(1)
'foo-01'
"""
input = field
expr = []
while '$' in field :
# defaults
offset = 0
width = 0
base = 'd'
escape = False
# different forms
if '${' in field :
pre, body = field.split('${', 1)
body, post = body.split('}', 1)
# parse body
parts = body.split(',')
# offset
offset = int(parts.pop(0))
# width
if parts :
width = int(parts.pop(0))
# base
if parts :
base = parts.pop(0)
if parts:
# fail
raise ValueError("extra data in ${...} body: {0!r}", parts)
elif '$$' in field :
pre, post = field.split('$$', 1)
escape = True
elif '\\$' in field :
pre, post = field.split('\\$', 1)
escape = True
else :
pre, post = field.split('$', 1)
expr.append(pre)
if escape :
expr.append('$')
else :
# meta-format
fmt = '{value[%d]:0%d%s}' % (offset, width, base)
log.debug("field=%r -> pre=%r, fmt=%r, post=%r", field, expr, fmt, post)
expr.append(fmt)
field = post
# final
if field :
expr.append(field)
# combine
expr = ''.join(expr)
log.debug("%s: %s", input, expr)
# processed
def value_func (value) :
# magic wrapper to implement offsets
return expr.format(value=OffsetValue(value))
return value_func
def parse_generate_range (field) :
"""
Parse a <start>-<stop>[/<step>] field
"""
if '/' in field :
field, step = field.split('/')
step = int(step)
else :
step = 1
start, stop = field.split('-')
start = int(start)
stop = int(stop)
log.debug(" range: start=%r, stop=%r, step=%r", start, stop, step)
# inclusive
return range(start, stop + 1, step)