--- a/README.md Mon Mar 02 21:47:08 2015 +0200
+++ b/README.md Tue Mar 03 13:07:31 2015 +0200
@@ -271,9 +271,18 @@
Use the *update* script to generate a complete set of output zonefiles:
- $ ./bin/update
+ $ ./bin/update -C
+ var: apply dir
+ var/dhcp: apply dir
+ var/zones: apply dir
+ var/include-cache: apply dir
+ var/serials: apply dir
+ var/dhcp/hosts: apply dir
+ var/zones/includes: apply dir
+ var/zones/forward: apply dir
+ var/zones/reverse: apply dir
Commit...
- Using commit timestamp: 1425049508
+ Using commit timestamp: 1425379711
Updating forward host zones...
var/zones/forward/test: Generating forward hosts zone: etc/zones/forward/test
Updating reverse host zones...
@@ -281,15 +290,131 @@
Updating DHCP hosts...
Copying zone includes...
Updating zones...
- var/serials/test: Update serial: 1425049508 <- 1425049508
- var/zones/test: Generate zone: etc/zones/test
+ var/serials/test: Update serial: <- 1425379711
+ var/zones/test: Generate zone: etc/zones/test @ 1425379711
Updating DHCP confs...
Testing zones...
Reload zones...
Reload zones
- rndc: server reload successful
+ * Reloading domain name service... bind9 [ OK ]
Testing DHCP...
Reload DHCP...
+ Reload DHCP
+ isc-dhcp-server stop/waiting
+ isc-dhcp-server start/running, process 32581
+
+The update script tracks hostfile/zonefile dependencies, and only updates the necessary output files:
+
+ $ touch etc/hosts/test.d/foo && ./bin/update -C
+ Commit...
+ Using commit timestamp: 1425379801
+ Updating forward host zones...
+ var/zones/forward/test: Generating forward hosts zone: etc/zones/forward/test
+ Updating reverse host zones...
+ var/zones/reverse/192.0.2: Generating reverse hosts zone: etc/zones/reverse/192.0.2
+ Updating DHCP hosts...
+ Copying zone includes...
+ Updating zones...
+ var/serials/test: Update serial: 1425379801 <- 1425379801
+ var/zones/test: Generate zone: etc/zones/test @ 1425379801
+ Updating DHCP confs...
+ Testing zones...
+ Reload zones...
+ Reload zones
+ * Reloading domain name service... bind9 ...done.
+ Testing DHCP...
+ Reload DHCP...
+ Reload DHCP
+ isc-dhcp-server stop/waiting
+ isc-dhcp-server start/running, process 775
+
+Use `-n` to enable noop mode and preview changes before updating:
+
+ sed -i s/quux/quux2/ etc/hosts/asdf.test && ./bin/update -C -n
+ Commit...
+ /home/tjmartti/pvl/pvl-hosts: skip commit
+ Using local unix time for uncommited changes: 1425380558
+ Updating forward host zones...
+ var/zones/forward/test: Generating forward hosts zone: etc/zones/forward/test
+ --- var/zones/forward/test 2015-03-03 12:55:53.480735624 +0200
+ +++ var/zones/forward/test.new 2015-03-03 13:02:38.708732551 +0200
+ @@ -2,2 +2,2 @@
+ bar A 192.0.2.2
+ -quux.asdf A 192.0.2.5
+ +quux22.asdf A 192.0.2.5
+ Updating reverse host zones...
+ var/zones/reverse/192.0.2: Generating reverse hosts zone: etc/zones/reverse/192.0.2
+ --- var/zones/reverse/192.0.2 2015-03-03 12:55:53.596735623 +0200
+ +++ var/zones/reverse/192.0.2.new 2015-03-03 13:02:38.832732550 +0200
+ @@ -2,2 +2,2 @@
+ 2 PTR bar.test.
+ -5 PTR quux.asdf.test.
+ +5 PTR quux22.asdf.test.
+ Updating DHCP hosts...
+ Copying zone includes...
+ Updating zones...
+ Updating DHCP confs...
+ Testing zones...
+ Reload zones...
+ Skip reload zones
+ Testing DHCP...
+ Reload DHCP...
+ Skip reload DHCP
+
+Note that noop mode does not yet handle dependency chains, i.e. you will not see which zones get updated serials without also using `-F`, which force-updates all output files regardless of dependency states.
+
+Finally, the default operation mode of update is to commit any changes, and update the zones using the commit timestamp as a serial. Use the `-p` flag to show output diffs as with `-n`:
+
+ $ sed -i s/quux/quux2/ etc/hosts/asdf.test && ./bin/update -m "rename quux to quux2" -p
+ Commit...
+ /home/tjmartti/pvl/pvl-hosts: commit: rename quux to quux2
+ diff -r 8790e1e28661 etc/hosts/asdf.test
+ --- a/etc/hosts/asdf.test Tue Mar 03 13:05:13 2015 +0200
+ +++ b/etc/hosts/asdf.test Tue Mar 03 13:06:03 2015 +0200
+ @@ -1,2 +1,2 @@
+ -[quux]
+ +[quux2]
+ ip = 192.0.2.5
+ Using commit timestamp: 1425380763
+ Updating forward host zones...
+ var/zones/forward/test: Generating forward hosts zone: etc/zones/forward/test
+ --- var/zones/forward/test 2015-03-03 13:04:09.556731909 +0200
+ +++ var/zones/forward/test.new 2015-03-03 13:06:04.260731122 +0200
+ @@ -2,2 +2,2 @@
+ bar A 192.0.2.2
+ -quux22.asdf A 192.0.2.5
+ +quux2.asdf A 192.0.2.5
+ Updating reverse host zones...
+ var/zones/reverse/192.0.2: Generating reverse hosts zone: etc/zones/reverse/192.0.2
+ --- var/zones/reverse/192.0.2 2015-03-03 13:04:09.684731908 +0200
+ +++ var/zones/reverse/192.0.2.new 2015-03-03 13:06:04.384731122 +0200
+ @@ -2,2 +2,2 @@
+ 2 PTR bar.test.
+ -5 PTR quux22.asdf.test.
+ +5 PTR quux2.asdf.test.
+ Updating DHCP hosts...
+ Copying zone includes...
+ Updating zones...
+ var/serials/test: Update serial: 1425380649 <- 1425380763
+ var/zones/test: Generate zone: etc/zones/test @ 1425380763
+ --- var/zones/test 2015-03-03 13:04:09.812731907 +0200
+ +++ var/zones/test.new 2015-03-03 13:06:04.512731121 +0200
+ @@ -1,3 +1,3 @@
+ $TTL 3600
+ -@ SOA foo.test. hostmaster.test. 1425380649 1d 5m 10d 300
+ +@ SOA foo.test. hostmaster.test. 1425380763 1d 5m 10d 300
+ NS foo
+ Updating DHCP confs...
+ Testing zones...
+ Reload zones...
+ Reload zones
+ * Reloading domain name service... bind9 [ OK ]
+ Testing DHCP...
+ Reload DHCP...
+ Reload DHCP
+ isc-dhcp-server stop/waiting
+ isc-dhcp-server start/running, process 2839
+
## Output zone files
--- a/bin/pvl.dns-includes Mon Mar 02 21:47:08 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-#!/usr/bin/env python
-
-"""
- Extract a list of $INCLUDE paths from a zone file.
-"""
-
-import logging; log = logging.getLogger('pvl.dns-includes')
-import optparse
-import pvl.args
-import pvl.dns
-import pvl.dns.process
-
-def main (argv):
- parser = optparse.OptionParser(main.__doc__)
- parser.add_option_group(pvl.args.parser(parser))
- parser.add_option_group(pvl.dns.process.optparser(parser))
-
- parser.add_option('--include-path', metavar='PATH',
- help="Rewrite includes to given absolute path")
-
- # input
- options, args = pvl.args.parse(parser, argv)
-
- # process
- zone = list(pvl.dns.process.apply_zone(options, args))
-
- if options.include_path:
- log.info("Set zone include path: %s", options.include_path)
-
- zone = list(pvl.dns.process.zone_includes_path(zone, options.include_path))
-
- for include in pvl.dns.process.zone_includes(zone):
- # output include path only
- print include
-
- return 0
-
-if __name__ == '__main__':
- pvl.args.main(main)
--- a/bin/pvl.dns-process Mon Mar 02 21:47:08 2015 +0200
+++ b/bin/pvl.dns-process Tue Mar 03 13:07:31 2015 +0200
@@ -22,12 +22,22 @@
parser.add_option('--include-path', metavar='PATH',
help="Rewrite includes to given absolute path")
+
+ parser.add_option('--include-trace', metavar='FILE',
+ help="Write out included files to given file")
# input
options, args = pvl.args.parse(parser, argv)
# process
- zone = list(pvl.dns.process.apply_zone(options, args))
+ if options.include_trace:
+ include_trace = [ ]
+ else:
+ include_trace = None
+
+ zone = list(pvl.dns.process.apply_zone(options, args,
+ include_trace = include_trace,
+ ))
if options.serial:
log.info("Set zone serial: %s", options.serial)
@@ -37,7 +47,14 @@
if options.include_path:
log.info("Set zone include path: %s", options.include_path)
- zone = list(pvl.dns.process.zone_includes_path(zone, options.include_path))
+ zone = list(pvl.dns.process.zone_includes(zone, options.include_path,
+ include_trace = include_trace,
+ ))
+
+ if options.include_trace:
+ with pvl.args.apply_file(options.include_trace, 'w') as file:
+ for include in include_trace:
+ print >>file, include
pvl.dns.process.apply_zone_output(options, zone)
--- a/lib/pvl/apply.sh Mon Mar 02 21:47:08 2015 +0200
+++ b/lib/pvl/apply.sh Tue Mar 03 13:07:31 2015 +0200
@@ -45,18 +45,18 @@
local update=
local out="$1"
- debug "$out"
+ log_debug "$out"
if [ ${#@} -eq 1 ]; then
- debug " update: empty deps"
- return 0
+ log_changed " update: empty deps"
+ return 2
elif [ ! -e "$out" ]; then
- debug " update: dest missing"
+ log_changed " update: dest missing"
return 2
elif [ "$APPLY_FORCE" = 1 ]; then
- debug " update: forced"
+ log_changed " update: forced"
return 2
fi
@@ -64,16 +64,16 @@
for dep in "${@:2}"; do
# check
if [ ! -e "$dep" ]; then
- warn "$out: Missing source: $dep"
+ warn "$out: Missing source: $dep"
elif [ "$out" -ot "$dep" ]; then
- debug " update: $dep"
+ log_changed " changed: $dep"
return 1
else
- debug " check: $dep"
+ log_debug " check: $dep"
fi
done
- debug " up-to-date"
+ log_debug " up-to-date"
return 0
}
--- a/lib/pvl/cmd.sh Mon Mar 02 21:47:08 2015 +0200
+++ b/lib/pvl/cmd.sh Tue Mar 03 13:07:31 2015 +0200
@@ -20,6 +20,7 @@
"$@"
}
+
## Execute command, prefixing its output on stdout with given indent prefix.
#
# indent " " $cmd...
@@ -32,3 +33,12 @@
return ${PIPESTATUS[0]}
}
+
+## Execute a command as root, using sudo if required.
+function cmd_sudo {
+ if [ $UID -eq 0 ]; then
+ cmd "$@"
+ else
+ cmd sudo "$@"
+ fi
+}
--- a/lib/pvl/hosts/dhcp.sh Mon Mar 02 21:47:08 2015 +0200
+++ b/lib/pvl/hosts/dhcp.sh Tue Mar 03 13:07:31 2015 +0200
@@ -11,16 +11,18 @@
function update_hosts_dhcp {
local out="$1"
local src="$2"
- local srcs=($(list_tree $src))
local msg="$out: Generating DHCP hosts: $src"
+ local include_cache=$(include_cache_path $src)
+ local srcs=($(include_cache $include_cache))
- if apply_check "$out" "${srcs[@]}"; then
+ if apply_check "$out" ${srcs[@]:-}; then
log_skip "$msg"
else
log_apply "$msg"
apply_cmd "$out" $OPT/bin/pvl.hosts-dhcp \
--hosts-include="$HOSTS_INCLUDE" \
+ --hosts-include-trace=$include_cache \
"$src"
fi
}
@@ -55,7 +57,7 @@
log_check "Checking DHCP: $DHCP_CONF"
- test_cmd "$conf" \
+ test_cmd "$DHCP_CONF" \
"$DHCP_SBIN" -cf "$DHCP_CONF" -t
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/pvl/hosts/include_cache.sh Tue Mar 03 13:07:31 2015 +0200
@@ -0,0 +1,26 @@
+INCLUDE_CACHE='var/include-cache'
+
+## Normalize a given source path for use in the include-cache
+function include_cache_path {
+ local src="$1"
+ local cache="$src"
+
+ cache="${cache//_/__}"
+ cache="${cache//\//_}"
+
+ echo "$INCLUDE_CACHE/$cache"
+}
+
+## Read the include cache for given path
+function include_cache {
+ local cache="$1"
+
+ if [ ! -e "$cache" ]; then
+ debug "missing: $cache"
+
+ return 0
+ fi
+
+ cat $cache
+}
+
--- a/lib/pvl/hosts/update.sh Mon Mar 02 21:47:08 2015 +0200
+++ b/lib/pvl/hosts/update.sh Tue Mar 03 13:07:31 2015 +0200
@@ -4,6 +4,7 @@
UPDATE_RELOAD=
UPDATE_INCLUDES=
+. $LIB/pvl/hosts/include_cache.sh
. $LIB/pvl/hosts/dhcp.sh
. $LIB/pvl/hosts/zone.sh
@@ -97,15 +98,7 @@
log "Updating zones..."
for zone in $(list_files etc/zones); do
- update_zone_includes "var/include-cache/$zone" "etc/zones/$zone"
-
- zone_includes=$(cat "var/include-cache/$zone")
-
- update_zone_serial "var/serials/$zone" $serial \
- "etc/zones/$zone" $zone_includes
-
- update_zone "var/zones/$zone" "etc/zones/$zone" "var/serials/$zone" \
- $zone_includes
+ update_zone -s "$serial" "var/zones/$zone" "etc/zones/$zone" "var/serials/$zone"
done
log "Updating DHCP confs..."
--- a/lib/pvl/hosts/zone.sh Mon Mar 02 21:47:08 2015 +0200
+++ b/lib/pvl/hosts/zone.sh Tue Mar 03 13:07:31 2015 +0200
@@ -5,10 +5,7 @@
ZONES_INCLUDE="$SRV/var/zones"
NAMED_CHECKZONE=/usr/sbin/named-checkzone
-
-RNDC=/usr/sbin/rndc
-RNDC_KEY=/etc/bind/rndc.key
-
+NAMED_SERVICE=bind9
## Generate forward zone from hosts hosts using pvl.hosts-forward
#
@@ -16,16 +13,18 @@
function update_hosts_forward {
local out="$1"
local src="$2"
- local srcs=($(list_tree $src))
local msg="$out: Generating forward hosts zone: $src"
+ local include_cache=$(include_cache_path $src)
+ local srcs=($(include_cache $include_cache))
- if apply_check "$out" "${srcs[@]}"; then
+ if apply_check "$out" ${srcs[@]:-}; then
log_skip "$msg"
else
log_apply "$msg"
apply_cmd "$out" $OPT/bin/pvl.hosts-forward \
- --hosts-include="$HOSTS_INCLUDE" \
+ --hosts-include=$HOSTS_INCLUDE \
+ --hosts-include-trace=$include_cache \
"$src"
fi
}
@@ -36,20 +35,26 @@
function update_hosts_reverse {
local out="$1"
local src="$2"
- local srcs=($(list_tree $src))
local msg="$out: Generating reverse hosts zone: $src"
+ local include_cache=$(include_cache_path $src)
+ local srcs=($(include_cache $include_cache))
- if apply_check "$out" "${srcs[@]}"; then
+ if apply_check "$out" ${srcs[@]:-}; then
log_skip "$msg"
else
log_apply "$msg"
apply_cmd "$out" $OPT/bin/pvl.hosts-reverse \
--hosts-include="$HOSTS_INCLUDE" \
+ --hosts-include-trace=$include_cache \
"$src"
fi
}
+## Update zone $INCLUDE file
+#
+# update_zone_include etc/zone/includes/$zone var/zone/includes/$zone
+#
function update_zone_include {
local out="$1"
local src="$2"
@@ -65,45 +70,15 @@
fi
}
-
-## Update list of zone $INCLUDEs from zone file
-#
-# update_zone_includes var/include-cache/$zone etc/zones/$zone
+## Check if the given zone needs to be updated; update serial if so
#
-function update_zone_includes {
- local out="$1"
- local src="$2"
- local includes="$ZONES_INCLUDE"
-
- if [ "$UPDATE_INCLUDES" = 1 ]; then
- log_force "$out: Force zone includes: $src"
-
- elif apply_check "$out" "$src"; then
- log_skip "$out: Skip zone includes: $src"
-
- return
-
- elif [ "$UPDATE_INCLUDES" = 0 ]; then
- log_noop "$out: Noop zone includes: $src"
-
- return
- else
- log_apply "$out: Update zone includes: $src"
- fi
-
- apply_cmd "$out" $OPT/bin/pvl.dns-includes \
- --include-path=$ZONES_INCLUDE \
- "$src"
-}
-
-## Update the cached .serial for the given zone, if the zone has changed:
-#
-# update_serial var/serials/$zone $serial $deps...
+# check_zone_serial var/serials/$zone $serial $zone_deps...
#
# Supports UPDATE_SERIAL=
-function update_zone_serial {
+function check_zone_serial {
local out="$1"
local serial="$2"
+ local srcs=(${@:3})
local old=$(test -e "$out" && cat "$out" || echo '')
@@ -111,51 +86,66 @@
if [ "$UPDATE_SERIAL" = 1 ]; then
log_force "$out: Force serial $old <- $serial"
- elif apply_check "$out" "${@:3}"; then
- log_skip "$out: Skip serial: $old <- $serial"
-
- return
+ elif apply_check "$out" ${srcs[@]:-}; then
+ return 0
elif [ "$UPDATE_SERIAL" = 0 ]; then
log_noop "$out: Noop serial: $old <- $serial"
- return
+ # fake
+ return 1
else
log_apply "$out: Update serial: $old <- $serial"
fi
echo "$serial" > $out
+
+ return 1
}
## Generate zone file from source using pvl.dns-process:
#
# update_zone var/zones/$zone etc/zones/$zone var/serials/$zone
#
-# Sets the SOA serial, and adjusts the $INCLUDE paths
+# Updates the SOA serial, and adjusts the $INCLUDE paths
function update_zone {
+ local update_serial=
+
+ local OPTIND
+ while getopts 's:' opt; do case $opt in
+ s) update_serial=$OPTARG ;;
+ esac done
+ shift $(($OPTIND - 1))
+
local out="$1"
local src="$2"
local serial="$3"
- local serial_opt=
- local msg="$out: Generate zone: $src"
+ local zone_serial=
- if [ -n "$serial" -a -f "$serial" ]; then
- serial_opt="--serial=$(cat "$serial")"
- elif [ "$UPDATE_SERIAL" = 0 ]; then
- warn "$out: omit noop'd serial"
+ local msg="$out: Generate zone: $src"
+ local include_cache=$(include_cache_path $src)
+ local srcs=($(include_cache $include_cache))
+
+ if check_zone_serial "$serial" $update_serial ${srcs[@]:-}; then
+ zone_serial=$(cat $serial)
+
+ log_skip "$out: Skip zone: $src @ $zone_serial <- $update_serial"
+
else
- fail "$out: missing serial: $serial"
- fi
+ zone_serial=$(cat $serial)
- if apply_check "$out" "${@:2}"; then
- log_skip "$msg"
- else
- log_apply "$msg"
+ # XXX: hack to get the right diff in NOOP mode
+ if [ "$UPDATE_SERIAL" = 0 ]; then
+ zone_serial=$update_serial
+ fi
+ log_apply "$out: Generate zone: $src @ $zone_serial"
+
apply_cmd "$out" $OPT/bin/pvl.dns-process \
- $serial_opt \
+ --serial=$zone_serial \
--include-path=$ZONES_INCLUDE \
+ --include-trace=$include_cache \
"$src"
fi
}
@@ -174,14 +164,9 @@
$NAMED_CHECKZONE $origin $zone
}
-# set by do_reload_zone if zone data has actually been reloaded
-RELOAD_ZONES=
-
## Load update zonefiles into bind:
#
# reload_zones
-#
-# Invokes `rndc reload`, showing its output.
function reload_zones {
if [ "$UPDATE_RELOAD" = 1 ]; then
log_force "Reload zones"
@@ -191,28 +176,14 @@
return
- elif [ ! -e "$RNDC" ]; then
- warn "Skip with missing RNDC: $RNDC"
-
- return
-
- elif [ ! -e "$RNDC_KEY" ]; then
- warn "Skip with missing RNDC_KEY: $RNDC_KEY"
+ elif ! service_status $NAMED_SERVICE; then
+ log_skip "named not running; did not restart"
return
-
- elif [ ! -r $RNDC_KEY ]; then
- error "Permission denied for RNDC_KEY: $RNDC_KEY"
-
- return 1
-
+
else
log_apply "Reload zones"
fi
- cmd_indent " rndc: " \
- $RNDC reload
-
- # set flag for dhcp
- RELOAD_ZONES=1
+ service_reload $NAMED_SERVICE
}
--- a/lib/pvl/log.sh Mon Mar 02 21:47:08 2015 +0200
+++ b/lib/pvl/log.sh Tue Mar 03 13:07:31 2015 +0200
@@ -8,6 +8,7 @@
LOG_FORCE=y
LOG_APPLY=y
LOG_CHECK=
+LOG_CHANGED=
LOG_NOOP=y
LOG_SKIP=
LOG_DEBUG=
@@ -41,6 +42,7 @@
LOG_DIFF=
;;
v)
+ LOG_CHANGED=y
LOG_CHECK=y
LOG_SKIP=y
;;
@@ -103,6 +105,10 @@
[ $LOG_CHECK ] && log_color '32' " $*" || true
}
+function log_changed {
+ [ $LOG_CHANGED ] && log_color '1;32' " $*" || true
+}
+
function log_noop {
[ $LOG_NOOP ] && log_color '2;34' " $*" || true
}
--- a/lib/pvl/service.sh Mon Mar 02 21:47:08 2015 +0200
+++ b/lib/pvl/service.sh Tue Mar 03 13:07:31 2015 +0200
@@ -25,8 +25,9 @@
function service_status {
local service=$1
-
- if [ $SERVICE_TYPE = upstart ]; then
+
+ # native upstart services don't exit with any status, but sysvinit compatibility ones do
+ if [ $SERVICE_TYPE = upstart -a -e /etc/init/$service.conf ]; then
cmd_test service $service status | grep -q start
else
cmd_test service $service status > /dev/null
@@ -35,6 +36,12 @@
function service_restart {
local service=$1
+
+ cmd_sudo service $service restart
+}
- cmd service $service restart
+function service_reload {
+ local service=$1
+
+ cmd_sudo service $service reload
}
--- a/pvl/dhcp/config.py Mon Mar 02 21:47:08 2015 +0200
+++ b/pvl/dhcp/config.py Tue Mar 03 13:07:31 2015 +0200
@@ -147,7 +147,11 @@
self.comment = comment
def __str__ (self):
- return ' '.join(self.key)
+ if self.key:
+ return ' '.join(self.key)
+ else:
+ # XXX: Item.__str__
+ return '; '.join(' '.join(str(x) for x in item) for item in self.items)
def __repr__ (self):
return "Block({self.key!r}, items={self.items!r}, blocks={self.blocks!r})".format(self=self)
--- a/pvl/dns/process.py Mon Mar 02 21:47:08 2015 +0200
+++ b/pvl/dns/process.py Tue Mar 03 13:07:31 2015 +0200
@@ -35,30 +35,28 @@
else:
yield rr
-def zone_includes_path (rrs, includes_path):
+def zone_includes (rrs, includes_path,
+ include_trace=None,
+):
"""
Rewrite include paths in zones.
+
+ include_trace - append included paths to given list
"""
for rr in rrs:
if isinstance(rr, zone.ZoneDirective) and rr.directive == 'INCLUDE':
include_path, = rr.arguments
- yield zone.ZoneDirective.INCLUDE(os.path.join(includes_path, include_path))
+ include = os.path.join(includes_path, include_path)
+
+ if include_trace is not None:
+ include_trace.append(include)
+
+ yield zone.ZoneDirective.INCLUDE(include)
else:
yield rr
-def zone_includes (rrs):
- """
- Extract $INCLUDE paths from zone.
- """
-
- for rr in rrs:
- if isinstance(rr, pvl.dns.ZoneDirective) and rr.directive == 'INCLUDE':
- include_path, = rr.arguments
-
- yield include_path
-
def apply_zone_output (options, zone):
"""
Output given ZoneDirective/ZoneRecord items to the output file/stdout.
@@ -70,9 +68,13 @@
file.write(unicode(item))
file.write('\n')
-def apply_zone (options, args):
+def apply_zone (options, args,
+ include_trace=None,
+):
"""
ZoneLine.load() in given zones.
+
+ include_trace - append included paths to given list
Yields ZoneDirective/ZoneRecord items.
"""
@@ -80,6 +82,9 @@
for file in pvl.args.apply_files(args, 'r', options.input_charset) :
log.info("%s: reading zone", file.name)
+ if include_trace is not None:
+ include_trace.append(file.name)
+
for item in zone.ZoneLine.load(file):
yield item
--- a/pvl/dns/tests.py Mon Mar 02 21:47:08 2015 +0200
+++ b/pvl/dns/tests.py Tue Mar 03 13:07:31 2015 +0200
@@ -204,9 +204,10 @@
$INCLUDE "includes/test"
"""))
-
+
+ include_trace = [ ]
rrs = list(process.zone_serial(rrs, 1337))
- rrs = list(process.zone_includes_path(rrs, '...'))
+ rrs = list(process.zone_includes(rrs, '...', include_trace))
self.assertZoneEqual(rrs, [
"$TTL 3600",
@@ -217,4 +218,7 @@
"bar A 192.0.2.2",
"$INCLUDE .../includes/test",
])
+ self.assertEqual(include_trace, [
+ '.../includes/test',
+ ])
--- a/pvl/hosts/__init__.py Mon Mar 02 21:47:08 2015 +0200
+++ b/pvl/hosts/__init__.py Tue Mar 03 13:07:31 2015 +0200
@@ -1,4 +1,4 @@
-__version__ = '0.8.0a6'
+__version__ = '0.8.0a8'
from pvl.hosts.config import (
optparser,
--- a/pvl/hosts/config.py Mon Mar 02 21:47:08 2015 +0200
+++ b/pvl/hosts/config.py Tue Mar 03 13:07:31 2015 +0200
@@ -22,6 +22,9 @@
hosts.add_option('--hosts-include', metavar='PATH',
help="Optional path for hosts includes, in addition to the host config dir")
+
+ hosts.add_option('--hosts-include-trace', metavar='FILE',
+ help="Write out all included file paths")
return hosts
@@ -207,7 +210,7 @@
log.info("%s: include: %s", config_path, path)
yield path
-def apply_hosts_configs (options, path, name, config, parent=None, defaults={}):
+def apply_hosts_configs (options, path, name, config, parent=None, defaults={}, include_trace=None):
"""
Load hosts from a configobj.Section (which can be the top-level ConfigObj).
@@ -217,6 +220,7 @@
config configobj.Section
parent parent section from included files or --hosts-domain
defaults hierarchial section defaults
+ include_trace - optional list to append loaded files to
"""
# items in this section
@@ -226,12 +230,15 @@
# process includes?
if 'include' in section:
- includes = section.pop('include').split()
+ # convert from unicode
+ includes = [str(include) for include in section.pop('include').split()]
includes = list(parse_config_includes(options, path, includes))
# within our domain context
- for host in apply_hosts_files(options, includes, parent=name, defaults=section):
+ for host in apply_hosts_files(options, includes, include_trace=include_trace,
+ parent=name, defaults=section
+ ):
yield host
else:
includes = None
@@ -304,26 +311,38 @@
return apply_hosts_configs(options, path, name, config, **opts)
-def apply_hosts_file (options, path, **opts):
+def apply_hosts_file (options, path, include_trace=None, **opts):
"""
Load Hosts from a file path.
+
+ include_trace - optional list to append loaded files to
"""
+
+ if include_trace is not None:
+ log.debug("%s: include trace", path)
+ include_trace.append(path)
try:
file = open(path)
except IOError as ex:
raise HostConfigError(path, ex.strerror)
- for host in apply_hosts_config(options, file, **opts):
+ for host in apply_hosts_config(options, file, include_trace=include_trace, **opts):
yield host
-def apply_hosts_directory (options, root, **opts):
+def apply_hosts_directory (options, root, include_trace=None, **opts):
"""
Load Hosts from a directory, loading each file within the directory.
+ include_trace - optional list to append loaded files to
+
Skips .dotfiles.
"""
+ if include_trace is not None:
+ log.debug("%s: include trace", root)
+ include_trace.append(root)
+
for name in sorted(os.listdir(root)):
path = os.path.join(root, name)
@@ -335,7 +354,7 @@
log.debug("%s: skip directory: %s", root, name)
continue
- for host in apply_hosts_file(options, path, **opts):
+ for host in apply_hosts_file(options, path, include_trace=include_trace, **opts):
yield host
def apply_hosts_files (options, files, **opts):
@@ -359,10 +378,16 @@
Exits with status=2 if loading the confs fails.
"""
+
+ if options.hosts_include_trace:
+ log.debug("include trace")
+ include_trace = [ ]
+ else:
+ include_trace = None
try:
# load hosts from configs
- hosts = list(apply_hosts_files(options, args))
+ hosts = list(apply_hosts_files(options, args, include_trace=include_trace))
except HostConfigObjError as error:
log.error("%s", error)
log.error("\t%s", error.line_contents)
@@ -371,6 +396,11 @@
except HostConfigError as error:
log.error("%s", error)
sys.exit(2)
+
+ if options.hosts_include_trace:
+ with pvl.args.apply_file(options.hosts_include_trace, 'w') as file:
+ for include in include_trace:
+ print >>file, include
# stable ordering
return sorted(hosts, key=Host.sort_key)
--- a/pvl/hosts/dhcp.py Mon Mar 02 21:47:08 2015 +0200
+++ b/pvl/hosts/dhcp.py Tue Mar 03 13:07:31 2015 +0200
@@ -76,9 +76,12 @@
extensions = host.extensions.get('dhcp', {})
for block in dhcp_host(host, **extensions):
- if block.key in blocks:
+ if not block.key:
+ # TODO: check for unique Item-Blocks
+ pass
+ elif block.key in blocks:
raise HostDHCPError(host, "dhcp {block} conflict with {other}; hosts on multiple networks must use unique ethernet.XXX=... naming".format(block=block, other=blocks[block.key]))
-
- blocks[block.key] = host
+ else:
+ blocks[block.key] = host
yield block
--- a/pvl/hosts/tests.py Mon Mar 02 21:47:08 2015 +0200
+++ b/pvl/hosts/tests.py Tue Mar 03 13:07:31 2015 +0200
@@ -18,6 +18,7 @@
hosts_charset = 'utf-8',
hosts_domain = None,
hosts_include = None,
+ hosts_include_trace = None,
)
def assertHostEqual(self, host, host_str, attrs):
@@ -185,7 +186,13 @@
def testApplyIncludePath(self):
self.options.hosts_include = 'etc/hosts'
- self.assertHostsEqual(config.apply_hosts_files(self.options, ['etc/zones/forward/test']), [
+ include_trace = [ ]
+
+ hosts = list(config.apply_hosts_files(self.options, ['etc/zones/forward/test'],
+ include_trace = include_trace,
+ ))
+
+ self.assertHostsEqual(hosts, [
('quux@asdf.test', dict(
ip = ipaddr.IPAddress('192.0.2.5'),
)),
@@ -197,6 +204,15 @@
)),
])
+ self.assertEqual(include_trace, [
+ 'etc/zones/forward/test',
+ 'etc/zones/forward/test/asdf.test',
+ 'etc/zones/forward/test/test',
+ 'etc/hosts/test.d/',
+ 'etc/hosts/test.d/bar',
+ 'etc/hosts/test.d/foo',
+ ])
+
def testApply(self):
self.assertHostsEqual(config.apply(self.options, ['etc/hosts/example.com']), [
('foo@example.com', dict(