pf-tools/pf-tools: 30 new changesets

parmelan-guest at users.alioth.debian.org parmelan-guest at users.alioth.debian.org
Thu Dec 2 17:47:41 UTC 2010


details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/ada57136eed1
changeset: 1083:ada57136eed1
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 08:15:32 2010 +0100
description:
Remove spurious whitespace in s{}{} replacement string

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/89b3e60f4950
changeset: 1084:89b3e60f4950
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 08:26:54 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/ffe648379649
changeset: 1085:ffe648379649
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 08:27:43 2010 +0100
description:
Drop unused function __get_key_value_from_context_key()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/5f4c893f7a1e
changeset: 1086:5f4c893f7a1e
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 08:34:51 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/505d01e5d95b
changeset: 1087:505d01e5d95b
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 08:38:38 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/819971d8052e
changeset: 1088:819971d8052e
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 08:43:36 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/aa439d6194db
changeset: 1089:aa439d6194db
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 10:25:13 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/92304c0e5933
changeset: 1090:92304c0e5933
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 10:29:12 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/1afc45fcf886
changeset: 1091:1afc45fcf886
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 19:35:11 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/7d84e1ba22ba
changeset: 1092:7d84e1ba22ba
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 19:40:44 2010 +0100
description:
Indent

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/8afd9ea3660e
changeset: 1093:8afd9ea3660e
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 19:46:51 2010 +0100
description:
get_suffix_from_ip_type(): use ipv4 as a default

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/64b749384fd1
changeset: 1094:64b749384fd1
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 19:53:11 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/fc5ca5dc047f
changeset: 1095:fc5ca5dc047f
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Thu Nov 25 20:02:12 2010 +0100
description:
Rename t/20.zone.t to t/20.files.t, to better match what it tests

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/f59618e833c8
changeset: 1096:f59618e833c8
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Fri Nov 26 18:12:55 2010 +0100
description:
get_cmdline_from_host_ref(): return an empty string rather than undef for $bond_cmdline

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/59737c58a539
changeset: 1097:59737c58a539
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Sat Nov 27 16:25:34 2010 +0100
description:
Mk_PXE_bootfile() replaced by make_preseed_file(), make_pxe_boot_and_preseed_files(), __build_pxe_boot() and __build_preseed().

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/b646fa23c17b
changeset: 1098:b646fa23c17b
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Sat Nov 27 16:49:10 2010 +0100
description:
Update mk_pxelinuxcfg to use the new functions

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/08d9608bb13d
changeset: 1099:08d9608bb13d
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Nov 29 14:10:21 2010 +0100
description:
Documentation for make_preseed_file()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/a36675475568
changeset: 1100:a36675475568
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Nov 29 14:40:33 2010 +0100
description:
Use make_path() in __make_file(), add documentation for make_boot_and_preseed_files() and __build_pxe_boot()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/61fe344e9e64
changeset: 1101:61fe344e9e64
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Nov 29 18:54:04 2010 +0100
description:
New function: __read_and_process_template() + use it in __build_preseed()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/6bace28fd417
changeset: 1102:6bace28fd417
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Nov 29 19:16:30 2010 +0100
description:
__build_pxe_boot(): move the initrd special handline to the template

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/a36ea10e7b1f
changeset: 1103:a36ea10e7b1f
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Nov 29 19:17:57 2010 +0100
description:
__build_pxe_boot(): use __read_and_process_template()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/90601f4b51e1
changeset: 1104:90601f4b51e1
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Nov 29 19:19:00 2010 +0100
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/c2c695a1fd0b
changeset: 1105:c2c695a1fd0b
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Nov 29 23:05:12 2010 +0100
description:
__build_sources_list(): use __read_and_process_template()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/a5efaf148dc2
changeset: 1106:a5efaf148dc2
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Nov 29 23:31:20 2010 +0100
description:
Flush2disk_GLOBAL() -> store_global_config(), Retrieve_GLOBAL() -> retrieve_global_config()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/974f85fabf93
changeset: 1107:974f85fabf93
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Tue Nov 30 07:54:55 2010 +0100
description:
Also test __build_sources_list() with backports = 1

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/b356965676de
changeset: 1108:b356965676de
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Tue Nov 30 08:24:55 2010 +0100
description:
mk_pxelinuxcfg: specify what we want to import from PFTools::Utils

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/3aa93028f1b3
changeset: 1109:3aa93028f1b3
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Tue Nov 30 08:45:48 2010 +0100
description:
Fix_hosts() -> fix_etc_hosts() + first tests

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/f52f6b324182
changeset: 1110:f52f6b324182
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Wed Dec 01 08:10:54 2010 +0100
description:
New check_args(), check_args_type() and check_true_args() functions

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/8f81e261c500
changeset: 1111:8f81e261c500
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Wed Dec 01 08:12:29 2010 +0100
description:
Args tests in get_site_list_from_hostname()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/de2d381543f5
changeset: 1112:de2d381543f5
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Wed Dec 01 08:25:18 2010 +0100
description:
fix_etc_hosts() / __fix_etc_hosts() separation, improvements and tests

diffstat:

14 files changed, 650 insertions(+), 423 deletions(-)
lib/PFTools.pm                |   43 ++
lib/PFTools/Conf.pm           |    1 
lib/PFTools/Conf/Host.pm      |    2 
lib/PFTools/Conf/Network.pm   |    2 
lib/PFTools/Conf/Syntax.pm    |    3 
lib/PFTools/Structqueries.pm  |    2 
lib/PFTools/Utils.pm          |    2 
sbin/mk_pxelinuxcfg           |    2 
t/12.storable.t               |    6 
t/20.files.t                  |  599 +++++++++++++++++++++++++++++++++++++++++
t/20.zone.t                   |  407 ---------------------------
templates/standard-installer  |    1 
templates/ubuntu-installer    |    1 
templates/ubuntu-sources.list |    2 

diffs (4233 lines):

diff -r 9e79004112bc -r de2d381543f5 lib/PFTools.pm
--- a/lib/PFTools.pm	Wed Nov 24 19:39:32 2010 +0100
+++ b/lib/PFTools.pm	Wed Dec 01 08:25:18 2010 +0100
@@ -22,12 +22,104 @@
 use warnings;
 
 use base qw( Exporter);
+use Carp;
 use English qw( -no_match_vars );    # Avoids regex performance penalty
 
-#our @EXPORT = qw();
+our @EXPORT = qw(
+    check_args_type
+    check_args
+    check_true_args
+);
+
 #our @EXPORT_OK = qw();
 
 our $VERSION = '1.0.1-WIP';
 
+
+my %type_string = (
+    q{}     => 'scalar',
+    'ARRAY' => 'arrayref',
+    'HASH'  => 'hashref',
+);
+
+=head2 FIXME
+=head2 check_hashref_args( $args_ref, $names_ref )
+
+Checks that the %{$args_ref} keys listed in @{$names_ref} all have a hashref
+value.
+
+=cut
+
+sub check_args_type {
+    my ($args_ref, $type, @keys) = @_;
+
+    if ( ref $args_ref ne 'HASH' or ref $type or not @keys ) {
+        confess q{ERROR: BUG: invalid args};
+    }
+
+    if ( $type eq 'SCALAR' ) {
+        $type = q{};
+    }
+
+    if ( not $type_string{$type} ) {
+        confess qq{ERROR: Unknown argument type '$type'};
+    }
+
+    foreach my $key (@keys) {
+        if ( not exists $args_ref->{$key} ) {
+            croak qq{ERROR: Mandatory argument $key not found};
+        }
+        if ( ref $args_ref->{$key} ne $type ) {
+            croak qq{ERROR: Invalid non-$type_string{$type} $key};
+        }
+    }
+
+    return 1;
+
+}
+
+=head2 check_args( $function_ref, $args_ref, @keys)
+
+Runs $function_ref->($args_ref, $key) for each key listed in @keys.
+
+This function is prototyped to allow a nice block syntax just as map, grep,
+and others:
+
+  check_args { some_code_here() } $args_ref, @keys;
+
+=cut
+
+sub check_args (&@$@) {
+    my ($function_ref, $args_ref, @keys) = @_;
+
+    foreach my $key (@keys) {
+        $function_ref->($args_ref, $key);
+    }
+
+    return 1;
+}
+
+=head2 check_true_args( $args_ref, $names_ref )
+
+Checks that all the %{$args_ref} keys listed in @{$names_ref} all have a true
+value. NOTE: this is therefor NOT suitable for 0, q{0}, etc.
+
+=cut
+
+sub check_true_args {
+    my ($args_ref, @keys) = @_;
+
+    check_args {
+        my ($args_ref, $key) = @_;
+
+        if ( not $args_ref->{$key} ) {
+            croak qq{ERROR: Invalid empty $key};
+        }
+    }
+    $args_ref, @keys;
+
+    return 1;
+}
+
 1;    # Magic true value required at end of module
 
diff -r 9e79004112bc -r de2d381543f5 lib/PFTools/Conf.pm
--- a/lib/PFTools/Conf.pm	Wed Nov 24 19:39:32 2010 +0100
+++ b/lib/PFTools/Conf.pm	Wed Dec 01 08:25:18 2010 +0100
@@ -48,8 +48,8 @@
     Init_PF_CONFIG
     Init_GLOBAL_NETCONFIG
     Load_conf
-    Flush2disk_GLOBAL
-    Retrieve_GLOBAL
+    store_global_config
+    retrieve_global_config
     Get_source
     Get_config_for_hostname_on_site
     get_default_pf_config
@@ -538,7 +538,7 @@
     return $GLOBAL;
 }
 
-=head2 Flush2disk_GLOBAL( $global_config, $pf_config, $flush_file )
+=head2 store_global_config( $global_config, $pf_config, $flush_file )
 
 Stores $global_config on disk in $flush_file (defaults to
 $pf_config->{'path'}->{'global_struct'}).
@@ -547,7 +547,7 @@
 
 =cut
 
-sub Flush2disk_GLOBAL {
+sub store_global_config {
     my ( $global_config, $pf_config, $flush_file ) = @_;
 
     if ( ref $global_config ne 'HASH' ) {
@@ -574,13 +574,13 @@
     return 1;
 }
 
-=head2 Retrieve_GLOBAL($path_global_file)
+=head2 retrieve_global_config( $path_global_file )
 
 Retrieves from $path_global_file and returns the stored $global_config.
 
 =cut
 
-sub Retrieve_GLOBAL {
+sub retrieve_global_config {
     my ($path_global_file) = @_;
 
     if ( ref $path_global_file ) {
diff -r 9e79004112bc -r de2d381543f5 lib/PFTools/Conf/Host.pm
--- a/lib/PFTools/Conf/Host.pm	Wed Nov 24 19:39:32 2010 +0100
+++ b/lib/PFTools/Conf/Host.pm	Wed Dec 01 08:25:18 2010 +0100
@@ -1130,12 +1130,11 @@
         ( is_private_vlan ($vlan, $global_config, $site) )
         ? $zone_part_ref->{'BY_SITE'}->{$site}
         : $zone_part_ref->{'ALL_SITES'};
-    my $added_hostclass = grep { m{ \A $hostclass \z }xms }
-        @{ $zone_entry->{'__hostclass_order'} };
 
-    if ( not defined $zone_entry->{$hostclass} and
-         not $added_hostclass
-    ) {
+    my $hostclass_already_added
+        = any { $_ eq $hostclass } @{ $zone_entry->{'__hostclass_order'} };
+
+    if ( not $zone_entry->{$hostclass} and not $hostclass_already_added ) {
         push @{ $zone_entry->{'__hostclass_order'} },
             $hostclass;
     }
diff -r 9e79004112bc -r de2d381543f5 lib/PFTools/Conf/Network.pm
--- a/lib/PFTools/Conf/Network.pm	Wed Nov 24 19:39:32 2010 +0100
+++ b/lib/PFTools/Conf/Network.pm	Wed Dec 01 08:25:18 2010 +0100
@@ -98,9 +98,9 @@
         }
 
         $zone_part->{$section_name} = {
-            'SOA'               => $section_ref,
-            'BY_SITE'           => {},
-            'ALL_SITES'         => {},
+            'SOA'       => $section_ref,
+            'BY_SITE'   => {},
+            'ALL_SITES' => {},
         };
     }
 
@@ -188,7 +188,7 @@
     foreach my $ip_type (qw( ipv4 ipv6 )) {
         next if not $pf_config->{'features'}->{$ip_type};
 
-        my $suffix    = get_suffix_from_ip_type( $ip_type );
+        my $suffix    = get_suffix_from_ip_type($ip_type);
         my $zone_part = $global_config->{"ZONE$suffix"};
         my $dhcp_part = $global_config->{"DHCP$suffix"};
         my $addr_key  = "BY_ADDR$suffix";
@@ -258,8 +258,9 @@
     my $net2add = {
         scope => $section_ref->{'scope'},
     };
-    if ( $section_ref->{'comment'} )
-        { $net2add->{'comment'} = $section_ref->{'comment'}; }
+    if ( $section_ref->{'comment'} ) {
+        $net2add->{'comment'} = $section_ref->{'comment'};
+    }
 
     # Check TAG
     my $tag = $section_ref->{'tag'};
@@ -272,7 +273,7 @@
     foreach my $ip_type (qw( ipv4 ipv6 )) {
         next if not $pf_config->{'features'}->{$ip_type};
 
-        my $suffix      = get_suffix_from_ip_type( $ip_type );
+        my $suffix      = get_suffix_from_ip_type($ip_type);
         my $subnet_ref  = get_subnet_from_vlan( $ip_type, $section_ref );
         my $zone_key    = qq{ZONE$suffix};
         my $dhcp_key    = qq{DHCP$suffix};
@@ -332,14 +333,14 @@
             # Adding entries for network, netmask, broadcast etc. into the
             # DNS zone
             my $add_args = {
-                'ref_net'       => $net2add,
+                'net_name'      => $section_name,
+                'net_ref'       => $net2add,
                 'subnet_ref'    => $subnet_ref,
-                'site'          => $site,
+                'site_name'     => $site,
                 'ip_type'       => $ip_type,
-                'net_name'      => $section_name,
                 'global_config' => $global_config,
             };
-            __add_network_in_zone_part( $add_args );
+            __add_network_in_zone_part($add_args);
 
             my $dhcp_part = $global_config->{$dhcp_key}->{'BY_SITE'}->{$site}
                 ->{$section_name};
@@ -363,7 +364,7 @@
 
 =over
 
-=item I<ref_net>
+=item I<net_ref>
 
 Reference to a hash containing the network to add
 
@@ -371,7 +372,7 @@
 
 NetAddr::IP object for the specified network
 
-=item I<site>
+=item I<site_name>
 
 String containing the site name of the specified network
 
@@ -390,34 +391,33 @@
 sub __add_network_in_zone_part {
     my ($args) = @_;
 
-    my ($ref_net, $subnet_ref, $site, $ip_type, $net_name, $global_config)
+    my ( $net_ref, $subnet_ref, $site_name, $ip_type, $net_name, $global_config )
         = @{$args}{
-        qw( ref_net subnet_ref site ip_type net_name global_config )
+        qw( net_ref subnet_ref site_name ip_type net_name global_config )
         };
 
     # Adding entries for network, netmask, broadcast etc. into the DNS zone
-    my $zone      = $global_config->{'SITE'}->{'BY_NAME'}->{$site}->{'zone'};
-    my $suffix    = get_suffix_from_ip_type( $ip_type );
+    my $zone      = $global_config->{'SITE'}->{'BY_NAME'}->{$site_name}->{'zone'};
+    my $suffix    = get_suffix_from_ip_type($ip_type);
     my $zone_key  = qq{ZONE$suffix};
     my $zone_part = $global_config->{$zone_key}->{'BY_NAME'}->{$zone};
     my $zone_ref  = {};
 
-    $zone_ref->{'network'} = qq{A\t} . $ref_net->{"network$suffix"};
-    $zone_ref->{'netmask'} = qq{A\t} . $ref_net->{"netmask$suffix"};
+    $zone_ref->{'network'} = qq{A\t} . $net_ref->{"network$suffix"};
+    $zone_ref->{'netmask'} = qq{A\t} . $net_ref->{"netmask$suffix"};
 
-    my $broadcast = $subnet_ref->broadcast();
-    $broadcast    =~ s{ [/].* \z }{}xms;    # remove /prefix
+    my $broadcast = $subnet_ref->broadcast()->addr();
     $zone_ref->{'broadcast'} = qq{A\t} . $broadcast;
 
-    if ( $ref_net->{"gateway$suffix"} ) {
-        $zone_ref->{'gateway'} = qq{A\t} . $ref_net->{"gateway$suffix"};
+    if ( $net_ref->{"gateway$suffix"} ) {
+        $zone_ref->{'gateway'} = qq{A\t} . $net_ref->{"gateway$suffix"};
     }
 
-    if ( is_private_vlan ($net_name, $global_config, $site) ) {
+    if ( is_private_vlan( $net_name, $global_config, $site_name ) ) {
         push
-            @{ $zone_part->{'BY_SITE'}->{$site}->{'__network_order'} },
+            @{ $zone_part->{'BY_SITE'}->{$site_name}->{'__network_order'} },
             $net_name;
-        $zone_part->{'BY_SITE'}->{$site}->{$net_name} = $zone_ref;
+        $zone_part->{'BY_SITE'}->{$site_name}->{$net_name} = $zone_ref;
     }
     else {
         $zone_part->{'ALL_SITES'}->{$net_name} = $zone_ref;
@@ -427,5 +427,6 @@
     return 1;
 }
 
+
 1;    # Magic true value required at end of module
 
diff -r 9e79004112bc -r de2d381543f5 lib/PFTools/Conf/Syntax.pm
--- a/lib/PFTools/Conf/Syntax.pm	Wed Nov 24 19:39:32 2010 +0100
+++ b/lib/PFTools/Conf/Syntax.pm	Wed Dec 01 08:25:18 2010 +0100
@@ -227,10 +227,10 @@
             'site'           => '(ALL|[\w\-]+(\s*,\s*[\w\-]+)*)',
             'number'         => '\d+',
             'order'          => '\d+',
-            'ipv4'      => '(\d{1,3})((\.\d{1,3}){1,3})?',
-            'ipv6'      => 'undefined',
-            'alias'     => '[a-z][a-z0-9\-]+[a-z0-9]',
-            'shortname' => '[a-z][a-z0-9\-]+[a-z0-9]',
+            'ipv4'           => '(\d{1,3})((\.\d{1,3}){1,3})?',
+            'ipv6'           => 'undefined',
+            'alias'          => '[a-z][a-z0-9\-]+[a-z0-9]',
+            'shortname'      => '[a-z][a-z0-9\-]+[a-z0-9]',
         },
         'service' => {
             'MANDATORY_KEYS' => [ 'site', '@host' ],
@@ -289,9 +289,9 @@
             'MANDATORY_KEYS' => [ 'source', 'fstype', 'options' ],
             'depends'        => 'undefined',
             'source'         => 'undefined',
-            'fstype'         => '(nfs|ext[2-4]|btrfs|cifs)',
-            'options'        => 'undefined',
-            'mode'           => '[0-7]?[0-7]{3}',
+            'fstype'  => '(nfs|ext[2-4]|btrfs|cifs)',
+            'options' => 'undefined',
+            'mode'    => '[0-7]?[0-7]{3}',
         },
         'installpkg' => {
             'depends'       => 'undefined',
@@ -344,13 +344,12 @@
         croak qq{ERROR: Invalid section type $section_type};
     }
 
-    my $definition       = $DEF_SECTIONS->{$int_context}->{$section_type};
-    my $parsed_keys_list = {};
-    my $check_mandatory  = 1;
+    my $definition           = $DEF_SECTIONS->{$int_context}->{$section_type};
+    my $parsed_keys_list     = {};
+    my $check_mandatory_keys = 1;
 
-    my $iface_type;
     if ( $section_type eq 'interface' ) {
-        ( $iface_type = $section_name )
+        ( my $iface_type = $section_name )
             =~ s{
                 \A
                     \Q$section_type\E
@@ -366,21 +365,23 @@
                     )?
                 \z
             }
-            {
-                $LAST_PAREN_MATCH{'iftype'}
-            }xms;
-        # FIXME : arbitrary set $parsed_keys_list->{'slaves'} to 1
-        # due to 'slaves' is a mandatory key on interface sections 
-        if ( $iface_type eq 'eth' ) { $parsed_keys_list->{'slaves'} = 1 };
+            {$LAST_PAREN_MATCH{'iftype'}}xms;
+
+        # FIXME arbitrary set $parsed_keys_list->{'slaves'} to 1
+        # due to 'slaves' is a mandatory key on interface sections
+        if ( $iface_type eq 'eth' ) {
+            $parsed_keys_list->{'slaves'} = 1
+        }
     }
 
     if ( $context eq 'model' ) {
         if ( $section_type eq 'hostgroup' ) {
-            $check_mandatory = 0;
+            $check_mandatory_keys = 0;
         }
+
+        # FIXME arbitrary set ipv4 and ipv6 to 1 when context is model
+        # due to ipv4 is mandatory in interface sections
         if ( $section_type eq 'interface' ) {
-            # FIXME : arbitrary set ipv4 and ipv6 to 1 when context is model
-            # due to ipv4 is mandatory in interface sections
             $parsed_keys_list->{'ipv4'} = 1;
         }
     }
@@ -388,35 +389,36 @@
     my $master_key = ( $context eq 'config' ) ? 'action' : 'type';
     foreach my $key ( keys %{$section_hash} ) {
         ( my $new = $key ) =~ s{ \A ([^.]+) (?: [.] .*)? \z }{$1}xmso;
-        my $def_key = 
-            ( $int_context eq 'host'
+        my $def_key = (
+            $int_context eq 'host'
                 or $context eq 'network' and $section_type eq 'server'
             )
             ? $new
             : $key;
 
         $parsed_keys_list->{$def_key} = 1;
-        
+
         # Next if useless checks
         next
             if $key eq $master_key
                 or $definition->{$def_key} eq 'undefined';
 
-        # Checking if key exits
+        # Check if key exists
         if ( not defined $definition->{$def_key} ) {
-            croak qq{ERROR: file $file, section $section_name: unknown key $key}
+            croak
+                qq{ERROR: file $file, section $section_name: unknown key $key}
         }
 
         my $value = ( ref $section_hash->{$key} eq 'ARRAY' )
             ? $section_hash->{$key}
             : [ $section_hash->{$key} ];
 
-        __check_key_value_from_definition (
+        __check_key_value_from_definition(
             $key, $value, $definition->{$def_key}, $section_name, $file
         );
     }
 
-    if ( $check_mandatory ) {
+    if ($check_mandatory_keys) {
         foreach my $key ( @{ $definition->{'MANDATORY_KEYS'} } ) {
             if ( not defined $parsed_keys_list ) {
                 croak
@@ -428,53 +430,38 @@
     return 1;
 }
 
-=head2 __get_key_value_from_context_key( $section, $context, $parsed_pkey )
+=head2 __check_key_value_from_definition( $key_name, $values_ref, $regexp, $section_name, $file_name )
 
-Retrieve key_name and values for checking on hash defintion structure.
-Returns a pair with key_name and tab_values
+Checks values for a specified key. Returns true value on success or croaks
+on errors. The parameters are:
 
-=cut
+=over
 
-sub __get_key_value_from_context_key {
-    my ( $section, $context, $parsed_key ) = @_;
+=item I<key_name> the key name
 
-    my ( $tab_values, $key_name );
-    if ( $context eq 'host' ) {
-        $tab_values
-            = ( $parsed_key !~ m{ \A [@] }xms )
-            ? [ $section->{$parsed_key}->{'VALUE'} ]
-            : $section->{$parsed_key}->{'VALUE'};
-        $key_name = $section->{$parsed_key}->{'ORIG_NAME'};
-    }
-    else {
-        $tab_values
-            = ( $parsed_key !~ m{ \A [@] }xms )
-            ? [ $section->{$parsed_key} ]
-            : $section->{$parsed_key};
-        $key_name = $parsed_key;
-    }
+=item I<values_ref> a reference to the values list
 
-    return ( $key_name, $tab_values );
-}
- 
-=head2 __check_key_value_from_definition( $key_name, $tab_values, $regexp, $sect_name, $file )
+=item I<regexp> the regexp agains which each value will be checked
 
-Check values for a specified key_name. Returns true value on success or croaks
-on errors
+=item I<section_name> the section name (for error messages)
+
+=item I<file_name> the file name (for error messages)
+
+=back
 
 =cut
 
 sub __check_key_value_from_definition {
-    my ( $key_name, $tab_values, $regexp, $sect_name, $file ) = @_;
+    my ( $key_name, $values_ref, $regexp, $section_name, $file_name ) = @_;
 
-    foreach my $value ( @{$tab_values} ) {
+    foreach my $value ( @{$values_ref} ) {
 
         # Remove surrounding whitespace
         $value =~ s{ \A \s* (\S*) \s* \z }{$1}xms;
 
         if ( $value !~ m{ \A $regexp \z }xms ) {
             croak
-                qq{Value '$value' for key $key_name in section $sect_name file $file doesn't match $regexp};
+                qq{ERROR: file $file_name, section $section_name, key $key_name: value '$value' doesn't match $regexp};
         }
     }
 
diff -r 9e79004112bc -r de2d381543f5 lib/PFTools/Structqueries.pm
--- a/lib/PFTools/Structqueries.pm	Wed Nov 24 19:39:32 2010 +0100
+++ b/lib/PFTools/Structqueries.pm	Wed Dec 01 08:25:18 2010 +0100
@@ -55,12 +55,14 @@
 =head2 get_suffix_from_ip_type ( $ip_type )
 
 This function returns the suffix according to an IP type on the global
-structure.
+structure. If not specified, I<$ip_type> defaults to 'ipv4'.
 
 =cut
 
 sub get_suffix_from_ip_type {
-    my ( $ip_type ) = @_;
+    my ($ip_type) = @_;
+
+    $ip_type ||= q{ipv4};    # default value
 
     my $suffix
         = $ip_type eq 'ipv6'
@@ -80,10 +82,10 @@
     my ( $hostname, $global_config ) = @_;
 
     my $list_ref = get_site_list_from_hostname( $hostname, $global_config );
-    unless ($list_ref) {
+    if ( not $list_ref) {
         croak qq{ERROR: Uknown host $hostname};
     }
-    if ( scalar @{$list_ref} > 1 ) {
+    if ( @{$list_ref} > 1 ) {
         croak qq{ERROR: Multiple sites for hostname $hostname};
     }
 
@@ -151,7 +153,8 @@
         }
     }
 
-    croak qq{ERROR: Unknown hostname $hostname} . ( $site ? qq{ on site $site} : q{} );
+    croak qq{ERROR: Unknown hostname $hostname}
+        . ( $site ? qq{ on site $site} : q{} );
 }
 
 =head2 get_iface_vlan_from_hostname( $vlan, $host_ref )
@@ -180,6 +183,17 @@
 
 sub get_site_list_from_hostname {
     my ( $hostname, $global_config ) = @_;
+
+    if ( not $hostname ) {
+        croak q{ERROR: Invalid empty hostname};
+    }
+    if ( ref $hostname ) {
+        croak q{ERROR: Invalid non-scalar hostname};
+    }
+
+    if ( ref $global_config ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref global_config};
+    }
 
     my @site_list = ();
     foreach my $site ( @{ $global_config->{'SITE'}->{'__site_list'} } ) {
@@ -242,7 +256,7 @@
 
 =head2 get_host_config( $hostname, $global_config, $site_name )
 
-This function returns a reference to the host config hash for I<$hostname.
+This function returns a reference to the host config hash for I<$hostname>.
 I<$site_name> is optional.
 
 =cut
@@ -257,7 +271,7 @@
 
     my $site_part = $global_config->{'SITE'}->{'BY_NAME'}->{$site_name};
     my $zone      = $site_part->{'zone'};
-    my ( $hostshort )
+    my ($hostshort)
         = $hostname =~ m{
             \A
                 (               # -> $hostshort
@@ -351,7 +365,7 @@
 sub get_pkgtype_from_hostname {
     my ( $hostname, $global_config, $site_name ) = @_;
 
-    my $host_props = get_host_config( $hostname, $global_config, $site_name);
+    my $host_props = get_host_config( $hostname, $global_config, $site_name );
 
     my %pkgtype_for = (
         debian => q{deb},
@@ -359,7 +373,7 @@
     );
 
     my $mode = $host_props->{'deployment'}->{'mode'};
-    if (not exists $pkgtype_for{$mode}) {
+    if ( not exists $pkgtype_for{$mode} ) {
         croak qq{ERROR: Unknown/not implemented deployment mode $mode};
     }
 
@@ -377,14 +391,16 @@
     my ($host_ref) = @_;
 
     my $cmdline = $host_ref->{'boot'}->{'cmdline'} || q{};
-    my $bond_cmdline;
+    my $bond_cmdline = q{};
     foreach my $iface ( sort keys %{ $host_ref->{'interfaces'} } ) {
         next if $iface !~ m{\A bond }xms;
 
         $bond_cmdline
             = qq/bonding.mode=$host_ref->{'interfaces'}->{$iface}->{'mode'}/;
-        foreach my $opt ( split qr{ \s* [,] \s* }xms,
-            $host_ref->{'interfaces'}->{$iface}->{'options'} )
+        foreach my $opt (
+            split qr{ \s* [,] \s* }xms,
+            $host_ref->{'interfaces'}->{$iface}->{'options'}
+            )
         {
             $bond_cmdline .= qq{ bonding.$opt};
         }
@@ -480,13 +496,13 @@
     my $zone_key = $ip_type eq 'ipv6' ? 'ZONE6' : 'ZONE';
     my $zone_ref = $global_config->{$zone_key}->{'BY_NAME'}->{$zone_name};
     my $zone_search;
-    if ( $hostvlan ) {
+    if ($hostvlan) {
         my $zone_part =
-            ( is_private_vlan ($hostvlan, $global_config, $site_name) )
+            ( is_private_vlan( $hostvlan, $global_config, $site_name ) )
             ? $zone_ref->{'BY_SITE'}->{$site_name}
             : $zone_ref->{'ALL_SITES'};
         return if ( not defined $zone_part->{$hostvlan} );
-        $zone_search = [ $zone_part ];
+        $zone_search = [$zone_part];
     }
     else {
         $zone_search = [
@@ -495,7 +511,7 @@
     }
 
     my @fields;
-    foreach my $zone_part ( @{ $zone_search } ) {
+    foreach my $zone_part ( @{$zone_search} ) {
         if ( $hostname =~ m{\A (network|netmask|broadcast|gateway) }xms ) {
             if ( $hostvlan and not $zone_part->{$hostvlan} ) {
                 return;
diff -r 9e79004112bc -r de2d381543f5 lib/PFTools/Utils.pm
--- a/lib/PFTools/Utils.pm	Wed Nov 24 19:39:32 2010 +0100
+++ b/lib/PFTools/Utils.pm	Wed Dec 01 08:25:18 2010 +0100
@@ -27,13 +27,18 @@
 use Carp;
 use Digest::MD5;
 use English qw( -no_match_vars );    # Avoids regex performance penalty
+use File::Basename;
 use File::Compare;
 use File::Copy;
+use File::Path qw( make_path );
 use File::Temp;
 use Hash::Merge::Simple qw( merge );
 use IO::File;
+use List::MoreUtils qw( uniq );
+use NetAddr::IP;
 use Template::Tiny;
 
+use PFTools;
 use PFTools::Conf;
 use PFTools::Logger;
 use PFTools::Net;
@@ -41,20 +46,23 @@
 use PFTools::Update;
 use PFTools::VCS;
 
+our @CARP_NOT = qw( PFTools );
+
 our @EXPORT = qw(
     Init_TOOLS
 
     Do_update_from_GLOBAL
-    Fix_hosts
     Mk_dhcp
-    Mk_PXE_bootfile
     Change_kopt_for_hostname
 
     Search_and_replace
 );
 
 our @EXPORT_OK = qw(
+    fix_etc_hosts
     make_interfaces_file
+    make_preseed_file
+    make_pxe_boot_and_preseed_files
     make_resolv_conf_file
     make_sources_list_file
     make_zone_file
@@ -68,7 +76,7 @@
     my ( $hostname, $pf_config_file, $global_store_file, $reload ) = @_;
 
     my $default_pf_config_file = q{/etc/pf-tools.conf};
-    if (not $pf_config_file and -e $default_pf_config_file) {
+    if ( not $pf_config_file and -e $default_pf_config_file ) {
         $pf_config_file = $default_pf_config_file;
     }
 
@@ -90,91 +98,178 @@
         }
 
         my $source
-            = Get_source( "COMMON:/$pf_config->{'path'}->{'start_file'}",
-            $hostname, {}, $pf_config );
+            = Get_source(
+            "COMMON:/$pf_config->{'path'}->{'start_file'}",
+            $hostname, {}, $pf_config
+            );
 
         $global_struct = Init_GLOBAL_NETCONFIG( $source, {}, $pf_config );
-        Flush2disk_GLOBAL( $global_struct, $pf_config );
+        store_global_config( $global_struct, $pf_config );
     }
     else {
-        $global_struct = Retrieve_GLOBAL($global_store_file);
+        $global_struct = retrieve_global_config( $global_store_file );
     }
 
     return ( $pf_config, $global_struct );
 }
 
-# FIXME convert to __make_file()
-# builds both the preseed and PXE files
-sub Mk_PXE_bootfile {
-    my ( $hostname, $host_ref, $pxe_template_filename, $preseed_template,
-        $default_preseed, $pf_script, $pf_config )
-        = @_;
+=head2 make_preseed_file($arguments_ref)
 
-    my $iface = get_iface_vlan_from_hostname(
-        $host_ref->{'deployment'}->{'dhcpvlan'}, $host_ref );
-    my $mac = $host_ref->{'interfaces'}->{$iface}->{'mac'};
-    ( my $pxe_boot_file = $mac ) =~ s{ [:] }{-}xmsg;
+Creates a preseed file for a given host. The preseed file is named
+"preseed_I<hostname>" and lies in the I<preseed_dir> directory as specified in
+the pf-tools configuration. This function returns the name (basename, not the
+complete path) of the written preseed file. It takes the following named
+arguments in %{$arguments_ref}:
 
-    my $pxe_template_content = __read_file_in_scalar($pxe_template_filename);
+=over
 
-    my $preseed_filename = __build_preseed_file(
+=item I<hostname> the hostname
+
+=item I<site_name> the site name
+
+=item I<global_config> a reference to the global configuration hash
+
+=item I<pf_config> a reference to the pf-tools configuration hash
+
+=item I<host_ref> a reference to the host configuration hash (optional:
+get_host_config() will be used if it is not specified)
+
+=item I<template_filename> the name of the preseed template file (optional,
+the default is computed from the host deployment mode and the pf-tools
+configuration)
+
+=back
+
+=cut
+
+sub make_preseed_file {
+    my ($arguments_ref) = @_;
+
+    if ( ref $arguments_ref ne 'HASH' ) {
+        croak q{ERROR: Invalid $arguments_ref};
+    }
+
+    my ($hostname, $site_name, $global_config,
+        $pf_config, $template_filename, $host_ref
+        )
+        = @{$arguments_ref}{
+        qw( hostname site_name global_config
+            pf_config template_filename host_ref )
+        };
+
+    $template_filename ||= $arguments_ref->{'preseed_template_filename'};
+
+    if ( not $hostname ) {
+        croak q{ERROR: Invalid empty $hostname};
+    }
+    if ( ref $hostname ) {
+        croak q{ERROR: Invalid non-scalar $hostname};
+    }
+
+    if ( ref $pf_config ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref $pf_config};
+    }
+    if ( not $pf_config->{'path'}->{'preseed_dir'} ) {
+        croak q{ERROR: Invalid hashref $pf_config: no ->path->preseed_dir};
+    }
+
+    $host_ref ||= get_host_config( $hostname, $global_config, $site_name );
+
+    my $mode = $host_ref->{'deployment'}->{'mode'};
+    my $templates_dirname = $pf_config->{'path'}->{'templates_dir'};
+
+    $template_filename ||= join q{/}, $templates_dirname,
+        (
+        $host_ref->{'deployment'}->{'preseed'} || $pf_config->{$mode}->{'preseed'}
+        );
+
+    my $basename = qq{preseed_$hostname};
+    my $filename = join q{/}, $pf_config->{'path'}->{'preseed_dir'},
+        $basename;
+
+    __make_file(
         {
-            hostname         => $hostname,
-            host_ref         => $host_ref,
-            preseed_template => $preseed_template,
-            default_preseed  => $default_preseed, # FIXME unused
-            pf_script        => $pf_script,
-            pf_config        => $pf_config,
+            %{$arguments_ref},
+            file_type         => q{preseed},
+            filename          => $filename,
+            template_filename => $template_filename,
+            host_ref          => $host_ref,
         }
     );
 
-    my $preseed_md5 = __get_md5sum_for_preseedfile( $preseed_filename, $pf_config );
+    return $basename;
+}
 
-    my $cmdline = join q{ }, get_cmdline_from_host_ref($host_ref);
-    $cmdline =~ s{ \A \s* }{}xms; # Remove leading white space
+=head2 make_boot_and_preseed_files($arguments_ref)
 
-    my $pxe_subst = {
-        'iface'        => $iface,
-        'mode'         => $host_ref->{'deployment'}->{'mode'} . '-installer',
-        'arch'         => $host_ref->{'deployment'}->{'arch'},
-        'distrib'      => $host_ref->{'deployment'}->{'distrib'},
-        'serial_speed' => '115200',
-        'preseed_url'  => $preseed_filename,
-        'preseed_md5'  => $preseed_md5,
-        'console'      => $host_ref->{'boot'}->{'console'},
-        'install_cmdline' => $host_ref->{'boot'}->{'cmdline'},
-        'cmdline'         => $cmdline,
-        'kernel'          => $host_ref->{'boot'}->{'kernel'}
-    };
+Creates a preseed file and a pxe boot file for a given host. The preseed
+file's name and MD5 hash are needed for the PXE boot file. The preseed file is
+created by calling make_preseed_file(), so its mandatory arguments are needed
+here too.
 
-    if ( $host_ref->{'boot'}->{'initrd'} ) {
-        $pxe_subst->{'initrd'} = $host_ref->{'boot'}->{'initrd'};
-    }
-    else {
-        $pxe_template_content =~ s{
-            initrd=
-            (
-                (
-                    [^/]+
-                    [/]
-                )+
-            )?
-            \[% \s* initrd \s* %\]
-        }{}xmsg;
+make_boot_and_preseed_files() returns a true value on success. It takes the
+following named arguments in %{$arguments_ref}:
+
+=over
+
+=item I<hostname> the hostname
+
+=item I<site_name> the site name
+
+=item I<global_config> a reference to the global configuration hash
+
+=item I<pf_config> a reference to the pf-tools configuration hash
+
+=item I<preseed_template_filename> the name of the preseed template file
+(optional, the default is computed from the host deployment mode and the
+pf-tools configuration)
+
+=item I<pxe_template_filename> the name of the pxe template file (optional,
+the default is computed from the host deployment mode and the pf-tools
+configuration)
+
+=back
+
+=cut
+
+sub make_pxe_boot_and_preseed_files {
+    my ($arguments_ref) = @_;
+
+    if ( ref $arguments_ref ne 'HASH' ) {
+        croak q{ERROR: Invalid $arguments_ref};
     }
 
-    my $tpl = Template::Tiny->new( TRIM => 1 );
-    my $pxe_content = q{};
-    $tpl->process( \$pxe_template_content, $pxe_subst, \$pxe_content );
+    my ( $hostname, $site_name, $global_config, $pf_config )
+        = @{$arguments_ref}{qw( hostname site_name global_config pf_config)};
 
-    my $tmp_fh = File::Temp->new( unlink => 0 );
-    my $tmp_fn = $tmp_fh->filename();
-    __write_scalar_to_filehandle($tmp_fh, $tmp_fn, $pxe_content);
+    $site_name ||= get_uniq_site_from_hostname( $hostname, $global_config, $pf_config );
 
-    my $dst = join q{/}, $pf_config->{'path'}->{'pxefiles_dir'}, $pxe_boot_file;
-    __move_if_different( $tmp_fn, $dst );
+    my $host_ref = get_host_config( $hostname, $global_config, $site_name );
 
-    return $pxe_boot_file;
+    my $preseed_filename = make_preseed_file(
+        {
+            %{$arguments_ref},
+            host_ref         => $host_ref,
+        }
+    );
+
+    my $iface = get_iface_vlan_from_hostname(
+        $host_ref->{'deployment'}->{'dhcpvlan'}, $host_ref
+    );
+    ( my $mac = $host_ref->{'interfaces'}->{$iface}->{'mac'} ) =~ s{ [:] }{-}xmsg;
+    my $pxe_boot_file = join q{/}, $pf_config->{'path'}->{'pxefiles_dir'}, $mac;
+
+    __make_file(
+        {
+            %{$arguments_ref},
+            file_type        => q{pxe_boot},
+            filename         => $pxe_boot_file,
+            site_name        => $site_name,
+            preseed_filename => $preseed_filename,
+        }
+    );
+
+    return 1;
 }
 
 =head2 make_zone_file($arguments_ref)
@@ -201,7 +296,7 @@
         croak q{ERROR: Invalid $arguments_ref};
     }
 
-    __make_file( { file_type => q{zone}, %{ $arguments_ref } } );
+    __make_file( { %{$arguments_ref}, file_type => q{zone} } );
 
     return 1;
 }
@@ -221,14 +316,15 @@
         croak q{ERROR: Invalid $arguments_ref};
     }
 
-    __make_file( { file_type => q{resolv.conf}, %{ $arguments_ref } } );
+    __make_file( { %{$arguments_ref}, file_type => q{resolv.conf} } );
 
     return 1;
 }
 
 sub Resolv {
-    my ( $type_resolve, $ip_type, $hostname, $global_config, $site_name,
-        $hosttype )
+    my ($type_resolve, $ip_type, $hostname, $global_config, $site_name,
+        $hosttype
+        )
         = @_;
 
     if ( $ip_type ne q{ipv4} and $ip_type ne q{ipv6} ) {
@@ -249,7 +345,8 @@
 
     # $type_resolve eq q{cnf}
     $site_name ||= get_uniq_site_from_hostname( $hostname, $global_config );
-    my $zone = get_zone_from_hostname( $hostname, $global_config, $site_name );
+    my $zone
+        = get_zone_from_hostname( $hostname, $global_config, $site_name );
     $hostname =~ s{ [.] $zone \z}{}xms;
 
     # FIXME this regexp is also used in
@@ -268,16 +365,20 @@
         \z
     }xms;
 
-    if ( not defined $hosttype
+    if (not defined $hosttype
         and $hostshort
-        !~ m{\A (network|netmask|broadcast|gateway|prefix)}xms )
+        !~ m{\A (network|netmask|broadcast|gateway|prefix)}xms
+        )
     {
         $hosttype
-            = get_hosttype_from_hostname( $hostshort, $global_config,
-                $site_name );
+            = get_hosttype_from_hostname(
+            $hostshort, $global_config,
+            $site_name
+            );
     }
     elsif ( $hostshort eq q{prefix} ) {
-        my $vlan_def = get_vlan_config( $hostvlan, $global_config, $site_name );
+        my $vlan_def
+            = get_vlan_config( $hostvlan, $global_config, $site_name );
         my $subnet_ref = get_subnet_from_vlan( $ip_type, $vlan_def );
         my $prefix = $subnet_ref->masklen();
 
@@ -297,10 +398,10 @@
 }
 
 sub Search_and_replace {
-    my ($hostname, $site_name, $input_file, $type_replace, $pf_config, $separator,
+    my ($hostname,      $site_name,    $input_file,
+        $type_replace,  $pf_config,    $separator,
         $global_config, $type_resolve, $ip_type
-        )
-        = @_;
+    ) = @_;
 
     if ( $type_resolve && $type_resolve eq 'cnf' && !defined $global_config )
     {
@@ -318,8 +419,10 @@
     foreach my $line ( @{$src} ) {
         if ( $type_replace eq 'resolver' ) {
             $line
-                = __search_and_resolve_ip( $hostname, $ip_type, $site_name, $line,
-                $separator, $type_resolve, $subst, $global_config );
+                = __search_and_resolve_ip(
+                $hostname,  $ip_type,      $site_name, $line,
+                $separator, $type_resolve, $subst,     $global_config
+                );
         }
         elsif ( $type_replace eq 'iface' ) {
             $line = __search_and_resolve_iface( $line, $host_props, $subst );
@@ -337,36 +440,138 @@
     return \@result;
 }
 
-sub Fix_hosts {
-    my ( $hostname, $input_file, $site_name, $ip_type, $global_config, $pf_config )
-        = @_;
+=head2 fix_etc_hosts($arguments_ref)
 
+Fixes the IP address listed in an /etc/hosts-like file for a given hostname.
+This is useful when debian-installer puts there a localnet address, to replace
+it with the IP address of the host in the dhcp deployment subnet.
+
+The named arguments are:
+
+=over
+
+=item I<hostname> the host name
+
+=item I<site_name> (optional) the site name
+
+=item I<input_file> the file to read from (usually I</etc/hosts>)
+
+=item I<output_file> the file to write to (usually I</etc/hosts> too)
+
+=item I<ip_type> I<ipv4> or I<ipv6> (but only I<ipv4> is currently supported)
+
+=item I<global_config> a reference to the global configuration hash
+
+=item I<pf_config> a reference to the pf-tools configuration hash
+
+=back
+
+=cut
+
+sub fix_etc_hosts {
+    my ($arguments_ref) = @_;
+
+    unless ( ref $arguments_ref eq 'HASH' ) {
+        croak q{ERROR: Invalid $arguments_ref};
+    }
+
+    check_args_type( $arguments_ref, q{},
+        qw( input_filename output_filename ) );
+
+    check_true_args( $arguments_ref,
+        qw( input_filename output_filename ) );
+
+    __make_file(
+        {
+            %{$arguments_ref},
+            file_type => q{etc_hosts},
+            filename  => $arguments_ref->{'output_filename'},
+        }
+    );
+
+    return 1;
+}
+
+=head2 __fix_etc_hosts($arguments_ref)
+
+Reads an /etc/hosts-type file, fixes the IP address for a given hostname,
+returns a reference to the list of lines. THe named arguments are:
+
+=over
+
+=item I<hostname> the host name
+
+=item I<site_name> (optional) the site name
+
+=item I<input_file> the file to read from (usually I</etc/hosts>)
+
+=item I<ip_type> I<ipv4> or I<ipv6> (but only I<ipv4> is currently supported)
+
+=item I<global_config> a reference to the global configuration hash
+
+=item I<pf_config> a reference to the pf-tools configuration hash
+
+=back
+
+=cut
+
+# FIXME: move to end of file
+# FIXME: what is the problem exactly, is this really necessary?
+
+sub __fix_etc_hosts {
+    my ($arguments_ref) = @_;
+
+    my ($hostname, $site_name,     $input_filename,
+        $ip_type,  $global_config, $pf_config
+        )
+        = @{$arguments_ref}
+        {qw( hostname site_name input_filename
+            ip_type global_config pf_config )};
+
+    # $hostname, $site_name and $global_config will be checked by
+    # get_host_config()
+
+    # input_filename will be checked by __read_file_in_array()
+
+    # FIXME pf_config is not used for the moment
+
+    if ( not $ip_type ) {
+        croak q{ERROR: Invalid empty ip_type};
+    }
+    if ( ref $ip_type ) {
+        croak q{ERROR: Invalid non-scalar ip_type};
+    }
     if ( $ip_type ne q{ipv4} ) {
-        croak qq{ERROR: $ip_type is not implemented for fixing $input_file};
+        croak qq{ERROR: __fix_etc_hosts: ip_type '$ip_type' not implemented};
     }
 
     my $host_ref = get_host_config( $hostname, $global_config, $site_name );
     my $dhcp_iface = get_iface_vlan_from_hostname(
-        $host_ref->{'deployment'}->{'dhcpvlan'}, $host_ref );
+        $host_ref->{'deployment'}->{'dhcpvlan'}, $host_ref
+    );
 
-    ( my $ip_deploy = $host_ref->{'interfaces'}->{$dhcp_iface}->{'ipv4'} )
-        =~ s{ [/] [\d]+ \z }{}xms;    # remove CIDR prefix size
+    # This should work for IPv6 too...
+    my $ip_and_prefix = $host_ref->{'interfaces'}->{$dhcp_iface}->{$ip_type};
+    my $ip_deploy     = NetAddr::IP->new($ip_and_prefix)->addr();
 
-    my $tmp_hosts = __read_file_in_array( $input_file, 0 );
+    my $lines_ref = __read_file_in_array( $input_filename, 1 );
 
-    foreach my $line ( @{$tmp_hosts} ) {
+    foreach my $line ( @{$lines_ref} ) {
         next unless $line =~ m{ $hostname }xms;
+
         $line =~ s{ \A 127 [.] 0 [.] [\d]{1,3} [.] [\d]{1,3} }{$ip_deploy}xms;
     }
 
-    return $tmp_hosts;
+    return $lines_ref;
 }
 
+# FIXME convert to __make_file()
 sub Mk_dhcp {
     my ( $header_file, $site_part ) = @_;
-    my @dhcp_hosts  = ();
+    my @dhcp_hosts = ();
 
-    my $dhcp_headers = $header_file ? __read_file_in_array($header_file, 1) : [];
+    my $dhcp_headers
+        = $header_file ? __read_file_in_array( $header_file, 1 ) : [];
 
     my @dhcp_subnets = ();
     foreach my $vlan ( keys %{$site_part} ) {
@@ -375,7 +580,7 @@
 
         foreach my $hostclass ( keys %{ $site_part->{$vlan} } ) {
             next
-                if     $hostclass eq q{subnet}
+                if $hostclass eq q{subnet}
                     or $hostclass eq q{netmask}
                     or $hostclass eq q{routers};
 
@@ -428,7 +633,7 @@
         croak q{ERROR: Invalid $arguments_ref};
     }
 
-    __make_file( { file_type => q{sources.list}, %{ $arguments_ref } } );
+    __make_file( { %{$arguments_ref}, file_type => q{sources.list} } );
 
     return 1;
 }
@@ -452,7 +657,7 @@
     my $lines_ref = __read_file_in_array( $grub_src, 1 );
 
     foreach my $line ( @{$lines_ref} ) {
-        if (    $grub_version == 1
+        if ($grub_version == 1
             and $line =~ m{ \A [#] kopt=.* \z }xms
             or $grub_version == 2
             and $line =~ m{ \A GRUB_CMDLINE_LINUX_DEFAULT=".*" \z }xms
@@ -467,7 +672,7 @@
     }
 
     # Either STDOUT or a tempfile...
-    my ($dst_fh, $dst_fn);
+    my ( $dst_fh, $dst_fn );
     if ( $dst eq q{-} ) {
         $dst_fh = IO::File->new();
         unless ( $dst_fh->fdopen( fileno(STDOUT), q{>} ) ) {
@@ -517,7 +722,7 @@
         croak q{ERROR: Invalid $arguments_ref};
     }
 
-    __make_file( { file_type => q{interfaces}, %{ $arguments_ref } } );
+    __make_file( { %{$arguments_ref}, file_type => q{interfaces} } );
 
     return 1;
 }
@@ -548,13 +753,17 @@
     $hash_subst->{'DISTRIB'} = get_distrib_from_host_ref($host_props);
     $hash_subst->{'MODE'}    = get_mode_from_host_ref($host_props);
     $options->{'pkg_type'}
-        ||= get_pkgtype_from_hostname( $hostname, $global_config, $site_name );
+        ||= get_pkgtype_from_hostname(
+        $hostname, $global_config,
+        $site_name
+        );
 
     my $host_config = Get_config_for_hostname_on_site(
         $hostname, $site_name, $hash_subst, $global_config, $pf_config
     );
     unless ($host_config) {
-        croak qq{ERROR: Problem when parsing config for $hostname on $site_name};
+        croak
+            qq{ERROR: Problem when parsing config for $hostname on $site_name};
     }
 
     if ( !$pf_config->{'features'}->{'update'} ) {
@@ -564,7 +773,7 @@
     my @sortedkeys = sort { Sort_config_sections( $host_config, $a, $b ) }
         @{ $host_config->{'__sections_order'} };
 
-    $|          = 1;
+    $| = 1;
     my $errorcount = __do_updateloop(
         $host_config, $options, $hash_subst, $global_config, \@sortedkeys
     );
@@ -614,8 +823,9 @@
                             next;
                         }
 
-                        if ( $host_config->{$depend}->{'action'} eq
-                            'addmount' )
+                        if ($host_config->{$depend}->{'action'} eq
+                            'addmount'
+                            )
                         {
                             carp
                                 qq{WARN: [$section] depends on addmount [$depend], it may not work during install!};
@@ -630,7 +840,7 @@
                         print qq{<$section>} . join q{ }, @depends;
                     }
                     $errorcount += __do_updateloop(
-                        $host_config,   $options, $hash_subst,
+                        $host_config, $options, $hash_subst,
                         $global_config, \@depends
                     );
                 }
@@ -695,11 +905,11 @@
         }xmso
         )
     {
-        my $before       = $1;
-        my $back         = $3;
-        my $match        = $2 . $3 . $4;
-        my $matchback    = $2 . $4;
-        my $after        = $5;
+        my $before    = $1;
+        my $back      = $3;
+        my $match     = $2 . $3 . $4;
+        my $matchback = $2 . $4;
+        my $after     = $5;
 
         my $lengthbefore = defined $before ? length $before : 0;
 
@@ -713,7 +923,11 @@
         $match2 =~ s{ HOSTNAME }{$hostname}xmsg;
         $match2 =~ s{ POPNAME }{$hash_subst->{'POPNAME'}}xmsg;
 
-        my $resolved = Resolv( $type_resolve, $ip_type, $match2, $global_config, $site_name);
+        my $resolved
+            = Resolv(
+            $type_resolve, $ip_type, $match2, $global_config,
+            $site_name
+            );
         if ( @{$resolved} ) {
             if ( $separator eq q{DUPLICATE} ) {
                 my $templine = q{};
@@ -727,7 +941,7 @@
             }
             else {
                 my $replacement = join $separator, @{$resolved};
-                substr( $line, $lengthbefore, length $match, $replacement);
+                substr( $line, $lengthbefore, length $match, $replacement );
             }
         }
 
@@ -774,7 +988,8 @@
 
         my $lengthbefore = defined $before ? length $before : 0;
 
-        ( my $real_vlan = $vlan ) =~ s{ POPNAME }{$hash_subst->{'POPNAME'}}xms;
+        ( my $real_vlan = $vlan )
+            =~ s{ POPNAME }{$hash_subst->{'POPNAME'}}xms;
 
         my $eth = get_iface_vlan_from_hostname( $real_vlan, $host_props );
         if ( defined $eth ) {
@@ -797,9 +1012,9 @@
     return $line;
 }
 
-=head2 __build_preseed_file($arguments_ref)
+=head2 __build_preseed($arguments_ref)
 
-Builds the preseed file for a host, returns the filename.
+Builds the preseed file content for a host.
 I<$arguments_ref> is a reference to a hash of named parameters:
 
 =over
@@ -808,11 +1023,93 @@
 
 =item I<host_ref> a reference to the host configuration hash
 
-=item I<preseed_template> the template file name
-
-=item I<default_preseed> FIXME unused?
+=item I<preseed_template_filename> the template file name. This argument can
+be passed as I<template_filename> for simplicity.
 
 =item I<pf_script> FIXME ???
+
+=item I<pf_config> a reference to the pf-tools configuration hash
+
+=item I<global_config> a reference to the global configuration hash
+
+=back
+
+=cut
+
+sub __build_preseed {
+    my ($arguments_ref) = @_;
+
+    my ($hostname, $host_ref, $template_filename,
+        $pf_script
+        )
+        = @{$arguments_ref}{
+        qw( hostname host_ref template_filename
+            pf_script
+            )
+        };
+
+    $template_filename ||= $arguments_ref->{'preseed_template_filename'};
+
+    if ( not $hostname ) {
+        croak q{ERROR: Invalid empty hostname};
+    }
+    if ( ref $hostname ) {
+        croak q{ERROR: Invalid non-scalar hostname};
+    }
+
+    if ( ref $host_ref ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref host_ref};
+    }
+
+    if ( not $template_filename ) {
+        croak q{ERROR: Invalid empty template_filename};
+    }
+    if ( ref $template_filename ) {
+        croak q{ERROR: Invalid non-scalar template_filename};
+    }
+
+    if ( not $pf_script ) {
+        croak q{ERROR: Invalid empty pf_script};
+    }
+    if ( ref $pf_script ) {
+        croak q{ERROR: Invalid non-scalar pf_script};
+    }
+
+    my $kernel_pkg = __get_kpkg_from_kernel(
+        $host_ref->{'boot'}->{'kernel'},
+        $host_ref->{'deployment'}->{'mode'},
+    );
+
+    my $vars_ref = {
+        'kernelpkg'     => $kernel_pkg,
+        'mode'          => $host_ref->{'deployment'}->{'mode'},
+        'distrib'       => $host_ref->{'deployment'}->{'distrib'},
+        'config_script' => $pf_script,
+    };
+
+    my $content = __read_and_process_template( $template_filename, $vars_ref );
+
+    return [$content];
+}
+
+=head2 __build_pxe_boot($arguments_ref)
+
+Builds the PXE boot file content for a host and returns a reference to the
+array of lines. I<$arguments_ref> is a reference to a hash of named
+parameters:
+
+=over
+
+=item I<hostname> the host name
+
+=item I<site_name> the site name
+
+=item I<preseed_filename> the preseed file name
+
+=item I<pxe_template_filename> the pxe template file name. This argument can
+be passed as I<template_filename> for simplicity.
+
+=item I<global_config> a reference to the global configuration hash
 
 =item I<pf_config> a reference to the pf-tools configuration hash
 
@@ -820,44 +1117,108 @@
 
 =cut
 
-sub __build_preseed_file {
+sub __build_pxe_boot {
     my ($arguments_ref) = @_;
 
-    my ($hostname,        $host_ref,  $preseed_template,
-        $default_preseed, $pf_script, $pf_config
-        ) = @{$arguments_ref}{
+    my ($hostname, $site_name, $global_config,
+        $pf_config, $preseed_filename, $template_filename
+        )
+        = @{$arguments_ref}{
         qw(
-        hostname          host_ref    preseed_template
-        default_preseed   pf_script   pf_config
-        )
+            hostname site_name global_config
+            pf_config preseed_filename template_filename
+            )
         };
 
-    my $preseed_template_content = __read_file_in_scalar($preseed_template);
+    $template_filename = $arguments_ref->{'pxe_template_filename'};
 
-    my $kernel_pkg = __get_kpkg_from_kernel(
-        $host_ref->{'boot'}->{'kernel'},
-        $host_ref->{'deployment'}->{'mode'},
+    if ( not $hostname ) {
+        croak q{ERROR: Invalid empty $hostname};
+    }
+    if ( ref $hostname ) {
+        croak q{ERROR: Invalid non-scalar $hostname};
+    }
+
+    if ( not $site_name ) {
+        croak q{ERROR: Invalid empty $site_name};
+    }
+    if ( ref $site_name ) {
+        croak q{ERROR: Invalid non-scalar $site_name};
+    }
+
+    if ( not $global_config ) {
+        croak q{ERROR: Invalid empty $global_config};
+    }
+    if ( ref $global_config ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref $global_config};
+    }
+
+    # This is not a complete check but it will catch obvious errors,
+    # like $global_config referencing a non-config hash
+    if ( not exists $global_config->{'ZONE'} ) {
+        croak q{ERROR: Invalid $global_config hashref: no 'ZONE' key found};
+    }
+
+    if ( not $pf_config ) {
+        croak q{ERROR: Invalid empty $pf_config};
+    }
+    if ( ref $pf_config ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref $pf_config};
+    }
+
+    # This is not a complete check but it will catch obvious errors,
+    # like $pf_config referencing a non-config hash
+    if ( not exists $pf_config->{'vcs'} ) {
+        croak q{ERROR: Invalid $pf_config hashref: no 'vcs' key found};
+    }
+
+    if ( not $preseed_filename ) {
+        croak q{ERROR: Invalid empty $preseed_filename};
+    }
+    if ( ref $preseed_filename ) {
+        croak q{ERROR: Invalid non-scalar $preseed_filename};
+    }
+
+    if ( ref $template_filename ) {
+        croak q{ERROR: Invalid non-scalar $template_filename};
+    }
+
+    my $host_ref = get_host_config( $hostname, $global_config, $site_name );
+    my $mode = $host_ref->{'deployment'}->{'mode'};
+    my $templates_dirname = $pf_config->{'path'}->{'templates_dir'};
+
+    $template_filename ||= join q{/}, $templates_dirname, $pf_config->{$mode}->{'pxe'};
+
+    my $preseed_md5
+        = __get_md5sum_for_preseedfile( $preseed_filename, $pf_config );
+
+    my $iface = get_iface_vlan_from_hostname(
+        $host_ref->{'deployment'}->{'dhcpvlan'}, $host_ref
     );
 
-    my $preseed_subst = {
-        'kernelpkg'     => $kernel_pkg,
-        'mode'          => $host_ref->{'deployment'}->{'mode'},
-        'distrib'       => $host_ref->{'deployment'}->{'distrib'},
-        'config_script' => $pf_script,
+    # Get cmdline, remove leading and trailing whitespace
+    ( my $cmdline = join q{ }, get_cmdline_from_host_ref($host_ref) )
+        =~ s{ \A \s* (\S*) \s* \z}{$1}xms;
+
+    my $vars_ref = {
+        'iface'        => $iface,
+        'mode'         => $host_ref->{'deployment'}->{'mode'} . '-installer',
+        'arch'         => $host_ref->{'deployment'}->{'arch'},
+        'distrib'      => $host_ref->{'deployment'}->{'distrib'},
+        'serial_speed' => '115200',
+        'preseed_url'  => $preseed_filename,
+        'preseed_md5'  => $preseed_md5,
+        'console'      => $host_ref->{'boot'}->{'console'},
+        'install_cmdline' => $host_ref->{'boot'}->{'cmdline'},
+        'cmdline'         => $cmdline,
+        'kernel'          => $host_ref->{'boot'}->{'kernel'},
+        'initrd'          => $host_ref->{'boot'}->{'initrd'},
     };
-    my $preseed_content = q{};
-    my $tpl = Template::Tiny->new( TRIM => 1 );
-    $tpl->process( \$preseed_template_content, $preseed_subst, \$preseed_content );
 
-    my $tmp_fh = File::Temp->new( unlink => 0 );
-    my $tmp_fn = $tmp_fh->filename();
-    __write_scalar_to_filehandle($tmp_fh, $tmp_fn, $preseed_content);
+    my $content
+        = __read_and_process_template( $template_filename, $vars_ref );
 
-    my $preseed_filename = qq{preseed_$hostname};
-    my $dst = join q{/}, $pf_config->{'path'}->{'preseed_dir'}, $preseed_filename;
-    __move_if_different( $tmp_fn, $dst );
-
-    return $preseed_filename;
+    return [$content];
 }
 
 =head2 __build_interfaces($arguments_ref)
@@ -916,15 +1277,17 @@
         croak q{ERROR: Invalid non-scalar $site_name};
     }
 
-    my $host_ref   = get_host_config( $hostname, $global_config, $site_name );
+    my $host_ref = get_host_config( $hostname, $global_config, $site_name );
 
     my $interfaces = {};
     my $routes     = {};
     foreach my $iface ( 'lo', sort keys %{ $host_ref->{'interfaces'} } ) {
         push @{ $interfaces->{'__order'} }, $iface;
         $interfaces->{$iface}
-            = __build_interface_lines_ref( $iface, $routes, $host_ref,
-            $pf_config );
+            = __build_interface_lines_ref(
+            $iface, $routes, $host_ref,
+            $pf_config
+            );
     }
 
 DESTINATION:
@@ -972,7 +1335,7 @@
         q{},
     );
 
-    foreach my $iface ( @{ $interfaces->{'__order' } } ) {
+    foreach my $iface ( @{ $interfaces->{'__order'} } ) {
         push @lines, @{ $interfaces->{$iface} }, q{};
     }
 
@@ -987,21 +1350,23 @@
 =cut
 
 sub __build_interface_lines_ref {
-    my ($iface, $routes, $host_ref, $pf_config) = @_;
+    my ( $iface, $routes, $host_ref, $pf_config ) = @_;
 
     my @iface_lines;
 
     my $if_part = $host_ref->{'interfaces'}->{$iface};
     my $if_method
-        = $if_part->{'method'} ? $if_part->{'method'}
-        : $iface eq 'lo'       ? q{loopback}
-        :                        q{static};
+        = $if_part->{'method'}
+        ? $if_part->{'method'}
+        : $iface eq 'lo' ? q{loopback}
+        :                  q{static};
     push @iface_lines,
         qq{auto $iface},
         qq{iface $iface inet $if_method};
 
-    if ( ( $if_part->{'method'} and $if_part->{'method'} eq 'dhcp' )
-        or $iface eq 'lo' )
+    if (( $if_part->{'method'} and $if_part->{'method'} eq 'dhcp' )
+        or $iface eq 'lo'
+        )
     {
         return \@iface_lines;
     }
@@ -1036,8 +1401,9 @@
             push @iface_lines, qq{\tvlan_raw_device\t$raw_device};
 
             # Set MTU to 1496 unless told otherwise
-            if (    $if_part->{'iface_opt'}
-                and $if_part->{'iface_opt'} !~ m{ mtu }xms )
+            if ($if_part->{'iface_opt'}
+                and $if_part->{'iface_opt'} !~ m{ mtu }xms
+                )
             {
                 $if_part->{'iface_opt'} .= q{, mtu 1496};
             }
@@ -1048,8 +1414,10 @@
 
         # Options
         if ( $if_part->{'iface_opt'} ) {
-            foreach my $option ( split qr{ \s* [,] \s* }xms,
-                $if_part->{'iface_opt'} )
+            foreach my $option (
+                split qr{ \s* [,] \s* }xms,
+                $if_part->{'iface_opt'}
+                )
             {
                 push @iface_lines,
                     qq{\tup\t\t/sbin/ip link set $iface $option};
@@ -1103,7 +1471,8 @@
     }
 
     my $host_props = get_host_config( $hostname, $global_config, $site_name );
-    my $domain = get_zone_from_hostname( $hostname, $global_config, $site_name );
+    my $domain
+        = get_zone_from_hostname( $hostname, $global_config, $site_name );
 
     my @dns = split qr{ \s* [,] \s* }xms, $host_props->{'dns'}->{'resolver'};
 
@@ -1116,9 +1485,12 @@
         q{},
     );
 
-    foreach my $ip_type ( qw( ipv4 ipv6 ) ) {
+    foreach my $ip_type (qw( ipv4 ipv6 )) {
         foreach my $dns (@dns) {
-            my $resolved = Resolv( q{cnf}, $ip_type, $dns, $global_config, $site_name );
+            my $resolved = Resolv(
+                q{cnf}, $ip_type, $dns, $global_config,
+                $site_name
+            );
             foreach my $ip ( @{$resolved} ) {
                 push @lines, qq{nameserver $ip};
             }
@@ -1200,7 +1572,7 @@
     my @network_order = @{ $zone_part->{'__network_order'} };
     push @network_order,
         @{ $zone_ref->{'ALL_SITES'}->{'__network_order'} };
-    foreach my $network ( @network_order ) {
+    foreach my $network (@network_order) {
         my $head = qq{; $network};
         if ( $merged_zone_ref->{$network}->{'comment'} ) {
             $head .= ": $merged_zone_ref->{$network}->{'comment'}";
@@ -1230,16 +1602,13 @@
         q{;;============================================================================},
         q{};
 
-    my @hostclass_order = 
-        @{ $zone_ref->{'BY_SITE'}->{$site_name}->{'__hostclass_order'} };
-    foreach my $hc ( @{ $zone_ref->{'ALL_SITES'}->{'__hostclass_roder'} } ) {
-        my $exist = grep m{ \A $hc \z }xms, @hostclass_order;
-        if ( not $exist ) {
-            push @hostclass_order, $hc;
-        }
-    }
+    my @hostclass_order
+        = uniq(
+            @{ $zone_ref->{'BY_SITE'}->{$site_name}->{'__hostclass_order'} },
+            @{ $zone_ref->{'ALL_SITES'}->{'__hostclass_order'} },
+        );
 
-    foreach my $server ( @hostclass_order ) {
+    foreach my $server (@hostclass_order) {
         my $head = qq{; $server};
 
         if ( $merged_zone_ref->{$server}->{'comment'} ) {
@@ -1339,7 +1708,7 @@
     my @sections = ();
     foreach my $section ( @{$sections_ref} ) {
 
-        # Remove leading and trailink whitespace
+        # Remove leading and trailing whitespace
         $section =~ s{ \A \s* (\S*) \s* \z }{$1}xms;
         if ( not $section ) {
             croak
@@ -1370,34 +1739,18 @@
     $template ||= join q{/}, $pf_config->{'path'}->{'templates_dir'},
         $pf_config->{$deployment_mode}->{'sources_list'};
 
-    my $tpl              = Template::Tiny->new( TRIM => 1 );
-    my $sources_template = __read_file_in_scalar($template);
-    my $sources_subst    = {
-        'mode'    => $deployment_mode,
-        'distrib' => $host_ref->{'deployment'}->{'distrib'},
-        'default_sections' =>
+    my $vars_ref = {
+        mode    => $deployment_mode,
+        distrib => $host_ref->{'deployment'}->{'distrib'},
+        default_sections =>
             $pf_config->{$deployment_mode}->{'default_sections'},
-        'custom_sections' => $sections,
+        custom_sections => $sections,
+        backports       => $backports,
     };
-    my $sources_content = q{};
-    $tpl->process( \$sources_template, $sources_subst, \$sources_content );
 
-    # FIXME why not in the template?
-    if ($backports) {
-        my $dash_backports
-            = $deployment_mode eq q{debian} ? q{-backports} : q{};
-        my $back_src = $deployment_mode . $dash_backports;
-        $sources_content .= <<"BACKPORTS_TEXT";
+    my $content = __read_and_process_template( $template, $vars_ref );
 
-deb http://mirrors.private/$back_src $host_ref->{'deployment'}->{'distrib'}-backports $pf_config->{$deployment_mode}->{'default_sections'}
-
-BACKPORTS_TEXT
-    }
-
-    # This cannot be in the template because of the TRIM option
-    $sources_content .= qq{\n};
-
-    return [$sources_content];
+    return [$content];
 }
 
 =head2 __get_kpkg_from_kernel( $pxefilename, $deploymode )
@@ -1451,8 +1804,20 @@
 sub __get_md5sum_for_preseedfile {
     my ( $filename, $pf_config ) = @_;
 
-    my $file_path = join q{/}, $pf_config->{'path'}->{'preseed_dir'},
-        $filename;
+    if ( not $filename ) {
+        croak q{ERROR: Invalid empty filename};
+    }
+    if ( ref $pf_config ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref pf_config};
+    }
+    if ( not $pf_config->{'path'}->{'preseed_dir'} ) {
+        croak q{ERROR: Invalid empty preseed_dir};
+    }
+    if ( ref $pf_config->{'path'}->{'preseed_dir'} ) {
+        croak q{ERROR: Invalid non-scalar preseed_dir};
+    }
+
+    my $file_path = join q{/}, $pf_config->{'path'}->{'preseed_dir'}, $filename;
 
     my $fh = IO::File->new( $file_path, q{<} );
     unless ($fh) {
@@ -1556,7 +1921,7 @@
 =cut
 
 sub __move_if_different {
-    my ($source, $destination) = @_;
+    my ( $source, $destination ) = @_;
 
     if ( compare( $source, $destination ) ) {
         unless ( move( $source, $destination ) ) {
@@ -1583,6 +1948,14 @@
 sub __read_file_in_scalar {
     my ($filename) = @_;
 
+    if ( not $filename ) {
+        croak q{ERROR: Invalid empty $filename};
+    }
+
+    if ( ref $filename ) {
+        croak q{ERROR: Invalid non-scalar $filename};
+    }
+
     my $fh = IO::File->new( $filename, q{<} );
     unless ($fh) {
         croak qq{ERROR: open $filename: $OS_ERROR};
@@ -1607,9 +1980,16 @@
 =cut
 
 sub __read_file_in_array {
-    my ($filename, $chomp_wanted) = @_;
+    my ( $filename, $chomp_wanted ) = @_;
 
     $chomp_wanted = 1 unless defined $chomp_wanted;
+
+    if ( not $filename ) {
+        croak q{ERROR: Invalid empty filename};
+    }
+    if ( ref $filename ) {
+        croak q{ERROR: Invalid non-scalar filename};
+    }
 
     my $fh = IO::File->new( $filename, q{<} );
     unless ($fh) {
@@ -1617,7 +1997,7 @@
     }
 
     my @lines = ();
-    while ( defined ( my $line = $fh->getline() ) ) {
+    while ( defined( my $line = $fh->getline() ) ) {
         if ($chomp_wanted) {
             chomp $line;
         }
@@ -1639,10 +2019,9 @@
 =cut
 
 sub __write_scalar_to_filehandle {
-    my ($fh, $filename, $scalar) = @_;
+    my ( $fh, $filename, $scalar ) = @_;
 
-    # FIXME should be q{}, but wait for proper tests to really correct it
-    __write_array_to_filehandle( $fh, $filename, [$scalar], qq{\n} );
+    __write_array_to_filehandle( $fh, $filename, [$scalar], q{} );
 
     return 1;
 }
@@ -1666,7 +2045,10 @@
     # IO::File does not implement filename()
     #my $filename = $fh->filename();
 
-    unless ( $fh->print( join( $line_separator, @{$array_ref}), $line_separator ) ) {
+    unless (
+        $fh->print( join( $line_separator, @{$array_ref} ), $line_separator )
+        )
+    {
         croak qq{ERROR: print $filename: $OS_ERROR};
     }
 
@@ -1694,13 +2076,18 @@
 is different from the original content. STDOUT is also supported (if
 I<filename> is '-').
 
+The path to I<filename> is automagically created via make_path() if needed.
+
 =cut
 
 sub __make_file {
     my ($arguments_ref) = @_;
 
     my %build_content_for = (
+        etc_hosts       => \&__fix_etc_hosts,
         interfaces      => \&__build_interfaces,
+        preseed         => \&__build_preseed,
+        pxe_boot        => \&__build_pxe_boot,
         q{resolv.conf}  => \&__build_resolv_conf,
         q{sources.list} => \&__build_sources_list,
         zone            => \&__build_zone,
@@ -1732,7 +2119,7 @@
     my $lines_ref = $build_content_for{$file_type}->($arguments_ref);
 
     # Either STDOUT or a tempfile...
-    my ($out_fh, $out_fn);
+    my ( $out_fh, $out_fn );
     if ( $filename eq q{-} ) {
         $out_fh = IO::File->new();
         unless ( $out_fh->fdopen( fileno(STDOUT), q{>} ) ) {
@@ -1748,11 +2135,38 @@
     __write_array_to_filehandle( $out_fh, $out_fn, $lines_ref, qq{\n} );
 
     if ( $filename ne q{-} ) {
+        make_path( dirname($filename) );
         __move_if_different( $out_fn, $filename );
     }
 
     return 1;
 }
 
+=head2 __read_and_process_template( $template_filename, $vars_ref )
+
+Reads I<$template_filename> as a Template::Tiny template and process it with
+I<$vars_ref>. Returns the processed content.
+
+=cut
+
+sub __read_and_process_template {
+    my ( $template_filename, $vars_ref ) = @_;
+
+    # __read_file_in_scalar() will take care of the $template_filename checks
+
+    if ( ref $vars_ref ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref vars_ref};
+    }
+
+    my $template_content = __read_file_in_scalar($template_filename);
+
+    my $content  = q{};
+    my $template = Template::Tiny->new();
+    $template->process( \$template_content, $vars_ref, \$content );
+
+    return $content;
+}
+
+
 1;    # Magic true value required at end of module
 
diff -r 9e79004112bc -r de2d381543f5 sbin/fix_hosts
--- a/sbin/fix_hosts	Wed Nov 24 19:39:32 2010 +0100
+++ b/sbin/fix_hosts	Wed Dec 01 08:25:18 2010 +0100
@@ -28,7 +28,7 @@
 use Sys::Hostname;
 
 use PFTools::Structqueries;
-use PFTools::Utils;
+use PFTools::Utils qw( Init_TOOLS fix_etc_hosts );
 
 #################################
 # VARS
@@ -103,19 +103,17 @@
         || get_uniq_site_from_hostname( $options->{'host'}, $GLOBAL_STRUCT );
 }
 
-my $fixed_input = Fix_hosts(
-    $options->{'host'}, $options->{'input'},
-    $options->{'site'}, $options->{'type'}, $GLOBAL_STRUCT,
-    $PF_CONFIG );
-unless( $fixed_input ) {
-    die "An error occured during fixing file $options->{'input'}";
-}
+fix_etc_hosts(
+    {
+        hostname       => $options->{'host'},
+        site_name      => $options->{'site'},
+        input_filename => $options->{'input'},
+        ouput_filename => $options->{'output'},
+        ip_type        => $options->{'type'},
+        global_config  => $GLOBAL_STRUCT,
+        pf_config      => $PF_CONFIG,
+    }
+);
 
-my $output_fh = IO::File->new(">$options->{'output'}")
-    or die "Unable to open destination $options->{'output'} : $OS_ERROR";
-$output_fh->print ( join '', @{$fixed_input} )
-    or die "Unable to write to destination $options->{'output'} : $OS_ERROR";
-$output_fh->close()
-    or die "Unable to close destination $options->{'output'} : $OS_ERROR";
 
 exit 0;
diff -r 9e79004112bc -r de2d381543f5 sbin/mk_pxelinuxcfg
--- a/sbin/mk_pxelinuxcfg	Wed Nov 24 19:39:32 2010 +0100
+++ b/sbin/mk_pxelinuxcfg	Wed Dec 01 08:25:18 2010 +0100
@@ -28,16 +28,16 @@
 use Getopt::Long qw( :config ignore_case_always bundling );
 use File::Path qw( make_path );
 
-use PFTools::Utils;
+use PFTools::Utils qw( Init_TOOLS make_pxe_boot_and_preseed_files );
 
 ############################################
 # Vars
 
 my @options_specs = (
-    'help',
+    'config|c=s',
+    'help|h',
     'script=s',
     'site|s=s',
-    'config|c=s',
     'store=s',
 );
 
@@ -106,25 +106,35 @@
     die "Unknown site $options->{'site'}";
 }
 
+=for FIXME
+
+provide an iterator for all hostclasses on a site
+provide an iterator for all hosts in a hostclass on a site
+provide an iterator for all hosts on a site
+
+=> pseudo-code:
+
+my $host_iterator = get_iteror_for_hosts( $options->{'site'} );
+foreach my $host ( $host_iterator->get_next() ) {
+    make_pxe_boot_and_preseed_files( ... );
+}
+
+=cut
+
 my $site_part = $GLOBAL_STRUCT->{'SITE'}->{'BY_NAME'}->{$options->{'site'}};
 my $host_part = $site_part->{'HOST'}->{'BY_NAME'};
 foreach my $hostclass ( @{ $site_part->{'HOST'}->{'__hostclass_pxe'} } ) {
     foreach my $host ( keys %{ $host_part->{$hostclass} } ) {
         next if ( ref $host_part->{$hostclass}->{$host} ne 'HASH' );
-        my $mode
-            = $host_part->{$hostclass}->{$host}->{'deployment'}->{'mode'};
-        my $pxe_template = $PF_CONFIG->{'path'}->{'templates_dir'} . '/'
-            . $PF_CONFIG->{$mode}->{'pxe'};
-        my $preseed_file =
-               $host_part->{$hostclass}->{$host}->{'deployment'}->{'preseed'}
-            || $PF_CONFIG->{$mode}->{'preseed'};
-        my $preseed_tpl = $PF_CONFIG->{'path'}->{'templates_dir'} . '/'
-            . $preseed_file;
-        my $pxe_file = Mk_PXE_bootfile(
-            $host, $host_part->{$hostclass}->{$host},
-            $pxe_template, $preseed_tpl,
-            $DEFAULT_PRESEED, $options->{'script'}, $PF_CONFIG
-        );
+
+        my $args_ref = {
+            hostname              => $host,
+            site_name             => $options->{'site'},
+            global_config         => $GLOBAL_STRUCT,
+            pf_config             => $PF_CONFIG,
+            pf_script             => $options->{'script'},
+        };
+        make_pxe_boot_and_preseed_files($args_ref);
     }
 }
 
diff -r 9e79004112bc -r de2d381543f5 t/12.storable.t
--- a/t/12.storable.t	Wed Nov 24 19:39:32 2010 +0100
+++ b/t/12.storable.t	Wed Dec 01 08:25:18 2010 +0100
@@ -6,21 +6,21 @@
 use Test::Exception;
 use Test::More qw( no_plan );
 
-use PFTools::Conf qw( Flush2disk_GLOBAL Retrieve_GLOBAL );
+use PFTools::Conf qw( store_global_config retrieve_global_config );
 
-can_ok( 'PFTools::Conf', qw( Flush2disk_GLOBAL Retrieve_GLOBAL ) );
+can_ok( 'PFTools::Conf', qw( store_global_config retrieve_global_config ) );
 
-note('Testing PFTools::Conf::Flush2disk_GLOBAL');
+note('Testing PFTools::Conf::store_global_config');
 
-throws_ok { Flush2disk_GLOBAL() }
+throws_ok { store_global_config() }
     qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
     => q{Dies on non-hashref global_config};
 
-throws_ok { Flush2disk_GLOBAL( {} ) }
+throws_ok { store_global_config( {} ) }
     qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] pf_config }xms
     => q{Dies on non-hashref pf_config};
 
-throws_ok { Flush2disk_GLOBAL( {}, {}, {} ) }
+throws_ok { store_global_config( {}, {}, {} ) }
     qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] flush_file }xms
     => q{Dies on non-scalar flush_file};
 
@@ -35,7 +35,7 @@
 };
 my $pf_config = {};
 
-throws_ok { Flush2disk_GLOBAL( $global_config, $pf_config ) }
+throws_ok { store_global_config( $global_config, $pf_config ) }
     qr{\A ERROR: }xms
     => 'Dies with error message if empty $pf_config and empty $flush_file';
 
@@ -50,45 +50,45 @@
     },
 };
 
-my $result = Flush2disk_GLOBAL($global_config, $pf_config);
+my $result = store_global_config( $global_config, $pf_config );
 ok $result && -s $store1
     => 'Stores something in default path';
 
-$result = Flush2disk_GLOBAL($global_config, $pf_config, $store2);
+$result = store_global_config( $global_config, $pf_config, $store2 );
 ok $result && -s $store2
     => 'Stores something in explicit path';
 unlink $store2;
 
-$result = Flush2disk_GLOBAL($global_config, {}, $store2);
+$result = store_global_config( $global_config, {}, $store2 );
 ok $result && -s $store2
     => 'Stores something in explicit path even if empty $pf_config';
 
-throws_ok { Flush2disk_GLOBAL($global_config, $pf_config, $store3) }
+throws_ok { store_global_config( $global_config, $pf_config, $store3 ) }
     # "Can't" case differs for some versions of Perl
     qr{\A ERROR: [ ] [Cc]an't [ ] create }xms
     => 'Dies if cannot create';
 
 
-note('Testing PFTools::Conf::Retrieve_GLOBAL');
+note('Testing PFTools::Conf::retrieve_global_config');
 
-throws_ok { Retrieve_GLOBAL( {} ) }
+throws_ok { retrieve_global_config( {} ) }
     qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] path_global_file }xms
     => q{Dies on non-scalar flush_file};
 
-throws_ok { Retrieve_GLOBAL() }
+throws_ok { retrieve_global_config() }
     qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] path_global_file }xms
     => q{Dies on empty flush_file};
 
-throws_ok { Retrieve_GLOBAL($store3) }
+throws_ok { retrieve_global_config($store3) }
     qr{\A ERROR: }xms
     => 'Dies on inexistent file';
 
-$result = Retrieve_GLOBAL($store1);
+$result = retrieve_global_config($store1);
 is_deeply $result, $global_config
     => 'Store1 retrieval';
 unlink $store1;
 
-$result = Retrieve_GLOBAL($store2);
+$result = retrieve_global_config($store2);
 is_deeply $result, $global_config
     => 'Store2 retrieval';
 unlink $store2;
diff -r 9e79004112bc -r de2d381543f5 t/20.files.t
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/20.files.t	Wed Dec 01 08:25:18 2010 +0100
@@ -0,0 +1,1198 @@
+#!perl
+
+use strict;
+use warnings;
+
+use Cwd;
+use English qw( -no_match_vars );    # Avoids regex performance penalty
+use File::Basename;
+use File::Copy;
+use Sys::Hostname;
+use Test::Exception;
+use Test::More qw( no_plan );
+
+use PFTools::Conf;
+use PFTools::Structqueries qw( get_host_config );
+use PFTools::Utils qw( /^./ ); # import @EXPORT and @EXPORT_OK
+
+# Let's go back to our test configuration
+# FIXME: this depends on t/13.conf.t, but it probably should not.
+my $config_file = 't/13.conf.cfg1/etc/pf-tools.1.conf';
+my $pf_config = Init_PF_CONFIG($config_file);    # already tested OK in 13.conf.t
+
+my $hostname = hostname;
+my $hash_subst = Init_SUBST( $hostname, undef, $pf_config, 'private' );
+
+my $global_config = Init_GLOBAL_NETCONFIG( q{COMMON:private-network}, $hash_subst );
+
+########################################################################
+note('Testing PFTools::Utils::__make_zone_header');
+can_ok( 'PFTools::Utils', qw( __make_zone_header ) );
+
+throws_ok { PFTools::Utils::__make_zone_header(); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] zone_name }xms
+    => q{Dies if empty $zone_name};
+
+throws_ok { PFTools::Utils::__make_zone_header( {} ); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] zone_name }xms
+    => q{Dies if non-scalar $zone_name};
+
+throws_ok { PFTools::Utils::__make_zone_header( q{name} ); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] site_name }xms
+    => q{Dies if empty $site_name};
+
+throws_ok { PFTools::Utils::__make_zone_header( q{name}, {} ); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site_name }xms
+    => q{Dies if non-scalar $site_name};
+
+throws_ok { PFTools::Utils::__make_zone_header( q{name}, q{site} ); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] zone_ref }xms
+    => q{Dies if empty $zone_ref};
+
+throws_ok { PFTools::Utils::__make_zone_header( q{name}, q{site}, q{ref} ); }
+qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] zone_ref }xms
+    => q{Dies if non-hashref $zone_ref};
+
+throws_ok { PFTools::Utils::__make_zone_header( q{name}, q{site}, {} ); }
+qr{ \A ERROR: [ ] Invalid [ ] [\$] zone_ref [ ] hashref:
+    [ ] no [ ] 'SOA' [ ] key [ ] found }xms
+    => q{Dies if non-zone hashref $zone_ref};
+
+
+my $zone_ref = $global_config->{'ZONE'}->{'BY_NAME'}->{'private'};
+$zone_ref->{'SOA'}->{'serial'} = '1289575205';
+my $result = PFTools::Utils::__make_zone_header( 'private', 'cbv4-pfds', $zone_ref );
+my $expected_result = [
+    ';;',
+    ';; BIND configuration file for zone: private',
+    ';; Site: cbv4-pfds',
+    ';;',
+    ';; Internal management zone',
+    ';;============================================================================',
+    '',
+    '$TTL 1D      ; TTL (1 day)',
+    '@                             IN SOA'     . qq{\t} . 'Deploy00.private. dnsmaster at private (',
+    '                              1289575205' . qq{\t} . '; Serial',
+    '                              6H      ; Refresh (6 hours)',
+    '                              1H      ; Retry (1 hour)',
+    '                              7D      ; Expire (7 days)',
+    '                              1H      ; Negative TTL (1 hours)',
+    '                              )',
+    '',
+    '                              IN NS' . qq{\t} . 'deploy00.vlan-systeme.private.',
+    '                              IN NS' . qq{\t} . 'deploy01.vlan-systeme.private.',
+    '',
+    '                              IN MX' . qq{\t} . '1       mf.private.',
+    '                              IN MX' . qq{\t} . '2       mf00.private.',
+    '                              IN MX' . qq{\t} . '2       mf01.private.',
+    '',
+    '',
+];
+
+is_deeply $result, $expected_result
+    => q{Returns the expected result for zone 'private'}
+    or note explain $result;
+
+
+########################################################################
+note('Testing PFTools::Utils::__build_zone');
+can_ok( 'PFTools::Utils', qw( __build_zone ) );
+
+throws_ok { PFTools::Utils::__build_zone(); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] zone_name }xms
+    => q{Dies if empty $zone_name};
+
+throws_ok { PFTools::Utils::__build_zone( { zone_name => {} } ); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] zone_name }xms
+    => q{Dies if non-scalar $zone_name};
+
+throws_ok { PFTools::Utils::__build_zone( { zone_name => q{name} } ); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] site_name }xms
+    => q{Dies if empty $site_name};
+
+throws_ok {
+    PFTools::Utils::__build_zone(
+        {
+            zone_name => q{name},
+            site_name => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site_name }xms
+    => q{Dies if non-scalar $site_name};
+
+throws_ok {
+    PFTools::Utils::__build_zone(
+        {
+            zone_name => q{name},
+            site_name => q{site},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] global_config }xms
+    => q{Dies if empty $global_config};
+
+throws_ok {
+    PFTools::Utils::__build_zone(
+        {
+            zone_name     => q{name},
+            site_name     => q{site},
+            global_config => q{ref},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
+    => q{Dies if non-hashref $global_config};
+
+throws_ok {
+    PFTools::Utils::__build_zone(
+        {
+            zone_name     => q{name},
+            site_name     => q{site},
+            global_config => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] [\$] global_config [ ] hashref:
+    [ ] no [ ] 'ZONE' [ ] key [ ] found }xms
+    => q{Dies if non-config hashref $global_config};
+
+throws_ok {
+    PFTools::Utils::__build_zone(
+        {
+            zone_name     => q{name},
+            site_name     => q{site},
+            global_config => $global_config,
+        }
+    );
+}
+qr{ \A ERROR: [ ] Unknown [ ] zone_name: [ ] }xms
+    => q{Dies if unknown zone_name};
+
+throws_ok {
+    PFTools::Utils::__build_zone(
+        {
+            zone_name     => q{private},
+            site_name     => q{site},
+            global_config => $global_config
+        }
+    );
+}
+qr{ \A ERROR: [ ] Unknown [ ] site_name: [ ] }xms
+    => q{Dies if unknown site_name};
+
+$result = PFTools::Utils::__build_zone(
+    {
+        zone_name     => q{private},
+        site_name     => q{cbv4-pfds},
+        global_config => $global_config,
+    }
+);
+$expected_result = [
+    ';;',
+    ';; BIND configuration file for zone: private',
+    ';; Site: cbv4-pfds',
+    ';;',
+    ';; Internal management zone',
+    ';;============================================================================',
+    '',
+    '$TTL 1D      ; TTL (1 day)',
+    '@                             IN SOA	Deploy00.private. dnsmaster at private (',
+    '                              1289575205	; Serial',
+    '                              6H      ; Refresh (6 hours)',
+    '                              1H      ; Retry (1 hour)',
+    '                              7D      ; Expire (7 days)',
+    '                              1H      ; Negative TTL (1 hours)',
+    '                              )',
+    '',
+    '                              IN NS	deploy00.vlan-systeme.private.',
+    '                              IN NS	deploy01.vlan-systeme.private.',
+    '',
+    '                              IN MX	1       mf.private.',
+    '                              IN MX	2       mf00.private.',
+    '                              IN MX	2       mf01.private.',
+    '',
+    '',
+    ';;',
+    ';; Networks',
+    ';;============================================================================',
+    '',
+    '; vlan-systeme',
+    ';----------------------------------------------------------------------------',
+    'network.vlan-systeme          IN A	10.1.0.0',
+    'netmask.vlan-systeme          IN A	255.255.0.0',
+    'broadcast.vlan-systeme        IN A	10.1.255.255',
+    '',
+    '; vlan-pfds-int',
+    ';----------------------------------------------------------------------------',
+    'network.vlan-pfds-int         IN A	10.2.0.0',
+    'netmask.vlan-pfds-int         IN A	255.255.0.0',
+    'broadcast.vlan-pfds-int       IN A	10.2.255.255',
+    '',
+    '; vlan-admindsi',
+    ';----------------------------------------------------------------------------',
+    'network.vlan-admindsi         IN A	10.3.1.0',
+    'netmask.vlan-admindsi         IN A	255.255.255.0',
+    'broadcast.vlan-admindsi       IN A	10.3.1.255',
+    'gateway.vlan-admindsi         IN A	10.3.1.254',
+    '',
+    '; vlan-middledsi',
+    ';----------------------------------------------------------------------------',
+    'network.vlan-middledsi        IN A	10.3.2.0',
+    'netmask.vlan-middledsi        IN A	255.255.255.0',
+    'broadcast.vlan-middledsi      IN A	10.3.2.255',
+    'gateway.vlan-middledsi        IN A	10.3.2.254',
+    '',
+    '; vlan-pfds-ext',
+    ';----------------------------------------------------------------------------',
+    'network.vlan-pfds-ext         IN A	192.168.1.0',
+    'netmask.vlan-pfds-ext         IN A	255.255.255.0',
+    'broadcast.vlan-pfds-ext       IN A	192.168.1.255',
+    'gateway.vlan-pfds-ext         IN A	192.168.1.254',
+    '',
+    '',
+    '',
+    ';;',
+    ';; Servers',
+    ';;============================================================================',
+    '',
+    '; vip-spawn',
+    ';----------------------------------------------------------------------------',
+    'cvs                           IN CNAME	vip-spawn.vlan-systeme',
+    'mf                            IN CNAME	vip-spawn.vlan-systeme',
+    'mirrors                       IN CNAME	vip-spawn.vlan-systeme',
+    'nscache                       IN CNAME	vip-spawn.vlan-systeme',
+    'nsprivate                     IN CNAME	vip-spawn.vlan-systeme',
+    'vip-deploy                    IN CNAME	vip-spawn.vlan-systeme',
+    'vip-spawn.vlan-pfds-ext       IN A	192.168.1.99',
+    'vip-spawn.vlan-systeme        IN A	10.1.1.254',
+    '',
+    '; cbv4-pfds-filer',
+    ';----------------------------------------------------------------------------',
+    'cbv4-pfds-filer00.vlan-systeme IN A	10.1.2.0',
+    'cbv4-pfds-filer01.vlan-systeme IN A	10.1.2.1',
+    '',
+    '; cbv4-spawn',
+    ';----------------------------------------------------------------------------',
+    'cbv4-spawn                    IN CNAME	cbv4-spawn.vlan-systeme',
+    'cbv4-spawn.vlan-systeme       IN A	10.1.167.0',
+    'cbv4-spawn.vlan-systeme       IN A	10.1.167.1',
+    'cbv4-spawn00.vlan-admindsi    IN A	10.3.1.41',
+    'cbv4-spawn00.vlan-middledsi   IN A	10.3.2.41',
+    'cbv4-spawn00.vlan-pfds-ext    IN A	192.168.1.97',
+    'cbv4-spawn00.vlan-pfds-int    IN A	10.2.167.0',
+    'cbv4-spawn00.vlan-systeme     IN A	10.1.167.0',
+    'cbv4-spawn01.vlan-admindsi    IN A	10.3.1.42',
+    'cbv4-spawn01.vlan-middledsi   IN A	10.3.2.42',
+    'cbv4-spawn01.vlan-pfds-ext    IN A	192.168.1.98',
+    'cbv4-spawn01.vlan-pfds-int    IN A	10.2.167.1',
+    'cbv4-spawn01.vlan-systeme     IN A	10.1.167.1',
+    'ntp                           IN CNAME	cbv4-spawn.vlan-systeme',
+    'ntp00                         IN CNAME	cbv4-spawn00.vlan-systeme',
+    'ntp01                         IN CNAME	cbv4-spawn01.vlan-systeme',
+    'spawn                         IN CNAME	cbv4-spawn.vlan-systeme',
+    'spawn00                       IN CNAME	cbv4-spawn00.vlan-systeme',
+    'spawn01                       IN CNAME	cbv4-spawn01.vlan-systeme',
+    ''
+];
+
+is_deeply $result, $expected_result
+    => q{Returns the expected result for zone 'private'}
+    or note explain $result;
+
+
+########################################################################
+note('Testing PFTools::Utils::make_zone_file');
+can_ok( 'PFTools::Utils', qw( make_zone_file ) );
+
+throws_ok { make_zone_file(); }
+qr{ \A ERROR: [ ] Invalid [ ] [\$] arguments_ref }xms
+    => q{Dies if no args};
+
+my $test_output_file = q{test.zone};
+$result = make_zone_file(
+    {
+        zone_name     => q{private},
+        site_name     => q{cbv4-pfds},
+        filename      => $test_output_file,
+        global_config => $global_config,
+    }
+);
+ok $result => q{Returns true on success};
+
+$result = PFTools::Utils::__read_file_in_array( $test_output_file, 1 );
+
+is_deeply $result, $expected_result
+    => q{Returns the expected result for host cbv4-rdeploy01 site cbv4'}
+    or note explain $result;
+
+ok unlink($test_output_file)
+    => q{Removed the test-generated zone file};
+
+
+########################################################################
+note('Testing PFTools::Utils::make_resolv_conf_file');
+can_ok( 'PFTools::Utils', qw( make_resolv_conf_file ) );
+
+throws_ok { make_resolv_conf_file(); }
+qr{ \A ERROR: [ ] Invalid [ ] [\$] arguments_ref }xms
+    => q{Dies if no args};
+
+throws_ok { make_resolv_conf_file( {} ); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] filename }xms
+    => q{Dies if empty $filename};
+
+throws_ok { make_resolv_conf_file( { filename => {} } ); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] filename }xms
+    => q{Dies if non-scalar $filename};
+
+throws_ok { make_resolv_conf_file( { filename => q{filename} } ); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] hostname }xms
+    => q{Dies if empty $hostname};
+
+throws_ok {
+    make_resolv_conf_file(
+        {
+            filename => q{filename},
+            hostname => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] hostname }xms
+    => q{Dies if non-scalar $hostname};
+
+throws_ok {
+    make_resolv_conf_file(
+        {
+            filename => q{filename},
+            hostname => q{name},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] global_config}xms
+    => q{Dies if empty $global_config};
+
+throws_ok {
+    make_resolv_conf_file(
+        {
+            filename      => q{filename},
+            hostname      => q{name},
+            global_config => q{global_config},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config [ ]}xms
+    => q{Dies if non-hashref $global_config};
+
+throws_ok {
+    make_resolv_conf_file(
+        {
+            filename      => q{filename},
+            hostname      => q{name},
+            global_config => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] [\$] global_config [ ] hashref:
+    [ ] no [ ] 'ZONE' [ ] key [ ] found }xms
+    => q{Dies if non-config hashref $global_config};
+
+throws_ok {
+    make_resolv_conf_file(
+        {
+            filename      => q{filename},
+            hostname      => q{hostname},
+            global_config => $global_config,
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] site_name }xms
+    => q{Dies if empty $site_name};
+
+throws_ok {
+    make_resolv_conf_file(
+        {
+            filename      => q{filename},
+            hostname      => q{name},
+            global_config => $global_config,
+            site_name     => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site_name }xms
+    => q{Dies if non-scalar $site_name};
+
+$test_output_file = q{test.resolv.conf};
+$result           = make_resolv_conf_file(
+    {
+        hostname      => q{cbv4-rdeploy01},
+        global_config => $global_config,
+        site_name     => q{cbv4},
+        filename      => $test_output_file,
+    }
+);
+ok $result => q{Returns true on success};
+
+$result = PFTools::Utils::__read_file_in_array( $test_output_file, 1 );
+$expected_result = [
+    q{#},
+    q{# This file was auto-generated by mk_resolvconf -- DO NOT EDIT!},
+    q{#},
+    q{},
+    q{search private},
+    q{},
+    q{nameserver 10.1.167.0},
+    q{nameserver 10.1.167.1},
+    q{},
+];
+
+is_deeply $result, $expected_result
+    => q{Returns the expected result for host cbv4-rdeploy01 site cbv4'}
+    or note explain $result;
+
+ok unlink($test_output_file)
+    => q{Removed the test-generated resolv.conf file};
+
+
+########################################################################
+note('Testing PFTools::Utils::__build_interfaces');
+can_ok( 'PFTools::Utils', qw( __build_interfaces ) );
+
+throws_ok { PFTools::Utils::__build_interfaces(); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] hostname }xms
+    => q{Dies if empty $hostname};
+
+throws_ok { PFTools::Utils::__build_interfaces( { hostname => {} } ); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] hostname }xms
+    => q{Dies if non-scalar $hostname};
+
+throws_ok {
+    PFTools::Utils::__build_interfaces( { hostname => q{hostname} } );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] global_config }xms
+    => q{Dies if empty $global_config};
+
+throws_ok {
+    PFTools::Utils::__build_interfaces(
+        {
+            hostname      => q{hostname},
+            global_config => q{global_config},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
+    => q{Dies if non-hashref $global_config};
+
+throws_ok {
+    PFTools::Utils::__build_interfaces(
+        {
+            hostname      => q{name},
+            global_config => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] [\$] global_config [ ] hashref:
+    [ ] no [ ] 'ZONE' [ ] key [ ] found }xms
+    => q{Dies if non-config hashref $global_config};
+
+throws_ok {
+    PFTools::Utils::__build_interfaces(
+        {
+            hostname      => q{hostname},
+            global_config => $global_config,
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] pf_config }xms
+    => q{Dies if empty $pf_config};
+
+throws_ok {
+    PFTools::Utils::__build_interfaces(
+        {
+            hostname      => q{hostname},
+            global_config => $global_config,
+            pf_config     => q{pf_config},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] pf_config }xms
+    => q{Dies if non-hashref $pf_config};
+
+throws_ok {
+    PFTools::Utils::__build_interfaces(
+        {
+            hostname      => q{hostname},
+            global_config => $global_config,
+            pf_config     => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] [\$] pf_config [ ] hashref:
+    [ ] no [ ] 'vcs' [ ] key [ ] found }xms
+    => q{Dies if non-config hashref $pf_config};
+
+throws_ok {
+    PFTools::Utils::__build_interfaces(
+        {
+            hostname      => q{hostname},
+            global_config => $global_config,
+            pf_config     => $pf_config,
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] site_name }xms
+    => q{Dies if empty $site_name};
+
+throws_ok {
+    PFTools::Utils::__build_interfaces(
+        {
+            hostname      => q{hostname},
+            global_config => $global_config,
+            pf_config     => $pf_config,
+            site_name     => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site_name }xms
+    => q{Dies if non-scalar $site_name};
+
+$result = PFTools::Utils::__build_interfaces(
+    {
+        hostname      => q{cbv4-spawn01},
+        global_config => $global_config,
+        pf_config     => $pf_config,
+        site_name     => q{cbv4-pfds},
+    }
+);
+$expected_result = [
+    qq{#},
+    qq{# This file was auto-generated by mk_interfaces -- DO NOT EDIT!},
+    qq{#},
+    qq{},
+    qq{auto lo},
+    qq{iface lo inet loopback},
+    qq{},
+    qq{auto bond0},
+    qq{iface bond0 inet static},
+    qq{\tslaves\t\teth2 eth3},
+    qq{\taddress\t\t192.168.1.98},
+    qq{\tnetmask\t\t255.255.255.0},
+    qq{\tnetwork\t\t192.168.1.0},
+    qq{\tbroadcast\t192.168.1.255},
+    qq{},
+    qq{auto eth0},
+    qq{iface eth0 inet static},
+    qq{\taddress\t\t10.1.167.1},
+    qq{\tnetmask\t\t255.255.0.0},
+    qq{\tnetwork\t\t10.1.0.0},
+    qq{\tbroadcast\t10.1.255.255},
+    qq{},
+    qq{auto eth0.39},
+    qq{iface eth0.39 inet static},
+    qq{\taddress\t\t10.2.167.1},
+    qq{\tnetmask\t\t255.255.0.0},
+    qq{\tnetwork\t\t10.2.0.0},
+    qq{\tbroadcast\t10.2.255.255},
+    qq{\tvlan_raw_device\teth0},
+    qq{\tup\t\t/sbin/ip link set eth0.39 mtu 1496},
+    qq{},
+    qq{auto eth4},
+    qq{iface eth4 inet static},
+    qq{\taddress\t\t10.3.1.42},
+    qq{\tnetmask\t\t255.255.255.0},
+    qq{\tnetwork\t\t10.3.1.0},
+    qq{\tbroadcast\t10.3.1.255},
+    qq{},
+    qq{auto eth5},
+    qq{iface eth5 inet static},
+    qq{\taddress\t\t10.3.2.42},
+    qq{\tnetmask\t\t255.255.255.0},
+    qq{\tnetwork\t\t10.3.2.0},
+    qq{\tbroadcast\t10.3.2.255},
+    qq{},
+];
+
+is_deeply $result, $expected_result
+    => q{Returns the expected result for host cbv4-spawn01 site cbv4-pfds'}
+    or note explain $result;
+
+########################################################################
+note('Testing PFTools::Utils::make_interfaces_file');
+can_ok( 'PFTools::Utils', qw( make_interfaces_file ) );
+
+throws_ok { make_interfaces_file(); }
+qr{ \A ERROR: [ ] Invalid [ ] [\$] arguments_ref }xms
+    => q{Dies if no args};
+
+$test_output_file = q{test.interfaces};
+$result = make_interfaces_file(
+    {
+        hostname      => q{cbv4-spawn01},
+        global_config => $global_config,
+        pf_config     => $pf_config,
+        site_name     => q{cbv4-pfds},
+        filename      => $test_output_file,
+    }
+);
+ok $result => q{Returns true on success};
+
+$result = PFTools::Utils::__read_file_in_array( $test_output_file, 1 );
+
+is_deeply $result, $expected_result
+    => q{Returns the expected result for host cbv4-spawn01 site cbv4-pfds'}
+    or note explain $result;
+
+ok unlink($test_output_file)
+    => q{Removed the test-generated interfaces file};
+
+########################################################################
+note('Testing PFTools::Utils::__build_sources_list');
+can_ok( 'PFTools::Utils', qw( __build_sources_list ) );
+
+throws_ok { PFTools::Utils::__build_sources_list(); }
+qr{ \A ERROR: }xms
+    => q{Dies if no $arguments_ref};
+
+throws_ok { PFTools::Utils::__build_sources_list( {} ); }
+qr{ \A ERROR: }xms
+    => q{Dies if empty $arguments_ref};
+
+throws_ok { PFTools::Utils::__build_sources_list( { template => q{} } ); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] template }xms
+    => q{Dies if empty $template};
+
+throws_ok { PFTools::Utils::__build_sources_list( { template => {} } ); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] template }xms
+    => q{Dies if non-scalar $template};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        { template => q{template}, hostname => q{}, sections_ref => q{} }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] sections_ref }xms
+    => q{Dies if empty $sections_ref};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        { template => q{template}, hostname => q{}, sections_ref => {} }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-array-ref [ ] [\$] sections_ref }xms
+    => q{Dies if non-array-ref $sections_ref};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        { template => q{template}, hostname => q{}, sections_ref => [] }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty-array-ref [ ] [\$] sections_ref }xms
+    => q{Dies if empty-array-ref $sections_ref};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        { template => q{template}, hostname => q{}, sections_ref => [q{}] }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] or [ ] blank [ ] section [ ] in [ ] array-ref [ ] [\$] sections_ref }xms
+    => q{Dies if empty section in array-ref $sections_ref};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        { template => q{template}, hostname => q{}, sections_ref => [q{ }] }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] or [ ] blank [ ] section [ ] in [ ] array-ref [ ] [\$] sections_ref }xms
+    => q{Dies if blank section in array-ref $sections_ref};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        {
+            template     => q{template},
+            sections_ref => [q{section}],
+            backports    => {}
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] backports }xms
+    => q{Dies if non-scalar $backports};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        {
+            template      => q{template},
+            sections_ref  => [q{section}],
+            hostname      => q{hostname},
+            global_config => q{foo},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
+    => q{Dies if non-hashref $global_config};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        {
+            template      => q{template},
+            sections_ref  => [q{section}],
+            hostname      => q{hostname},
+            global_config => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] [\$] global_config [ ] hashref: }xms
+    => q{Dies if non-config hashref $global_config};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        {
+            template      => q{template},
+            sections_ref  => [q{section}],
+            global_config => $global_config,
+            hostname      => q{},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] hostname }xms
+    => q{Dies if empty $hostname};
+
+throws_ok {
+    PFTools::Utils::__build_sources_list(
+        {
+            template      => q{template},
+            sections_ref  => [q{section}],
+            global_config => $global_config,
+            hostname      => {},
+        }
+    );
+}
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] hostname }xms
+    => q{Dies if non-scalar $hostname};
+
+my $test_sections_ref = [qw(common uncommon)];
+my $args_ref = {
+    hostname      => q{cbv4-rdeploy01},
+    site_name     => q{cbv4},
+    sections_ref  => $test_sections_ref,
+    template      => q{templates/sources.list},
+    global_config => $global_config,
+    pf_config     => $pf_config,
+};
+$result = PFTools::Utils::__build_sources_list( $args_ref );
+
+my $expected_sources_list = <<'EOT';
+#
+# Generated by mk_sourceslist -- DO NOT EDIT!
+#
+
+deb http://mirrors.private/debian lenny main contrib non-free
+deb http://mirrors.private/debian-custom lenny-custom common uncommon
+deb http://mirrors.private/debian-security lenny/updates main contrib non-free
+
+EOT
+
+$expected_result = [$expected_sources_list];
+
+is_deeply $result, $expected_result
+    => q{Returns the expected content for host cbv4-rdeploy01 site cbv4'}
+    or note explain $result;
+
+$args_ref->{'backports'} = 1;
+$result = PFTools::Utils::__build_sources_list( $args_ref );
+$expected_sources_list .= <<'EOT';
+deb http://mirrors.private/debian-backports lenny-backports main contrib non-free
+
+EOT
+$expected_result = [$expected_sources_list];
+
+is_deeply $result, $expected_result
+    => q{Returns the expected content for host cbv4-rdeploy01 site cbv4 and backports'}
+    or note explain $result;
+
+########################################################################
+note('Testing PFTools::Utils::make_sources_list_file');
+can_ok( 'PFTools::Utils', qw( make_sources_list_file ) );
+
+throws_ok { make_sources_list_file(); }
+qr{ \A ERROR: [ ] Invalid [ ] [\$] arguments_ref }xms
+    => q{Dies if no args};
+
+$expected_result = [ split qr{ \n }xms, $expected_sources_list, -1 ];
+
+$test_output_file = q{test.sources.list};
+$result = make_sources_list_file(
+    {
+        filename => $test_output_file,
+        %{$args_ref},
+    }
+);
+ok $result => q{Returns true on success};
+
+$result = PFTools::Utils::__read_file_in_array( $test_output_file );
+
+is_deeply $result, $expected_result
+    => q{Returns the expected result for host cbv4-rdeploy01 site cbv4'}
+    or note explain $result;
+
+ok unlink($test_output_file)
+    => q{Removed the test-generated interfaces file};
+
+
+########################################################################
+note('Testing PFTools::Utils::make_preseed_file');
+can_ok( 'PFTools::Utils', qw( make_preseed_file ) );
+
+$hostname = q{cbv4-rdeploy01};
+my $site_name = q{cbv4};
+
+$expected_result = [ split qr{ \n }xms, <<'EOT', -1 ];
+#### Contents of the preconfiguration file
+d-i debconf/priority select critical
+
+### Localization
+# Locale sets language and country.
+d-i debian-installer/locale string en_US
+
+# Keyboard selection.
+d-i console-keymaps-at/keymap select fr
+
+### Network configuration
+#d-i netcfg/choose_interface select auto
+#d-i interface select eth0
+d-i interface string eth0
+d-i interface seen false
+d-i netcfg/get_hostname string unassigned-hostname
+d-i netcfg/get_domain string unassigned-domain
+d-i netcfg/no_default_route boolean true
+# Disable that annoying WEP key dialog.
+d-i netcfg/wireless_wep string
+
+### Mirror settings
+d-i mirror/protocol string http
+d-i mirror/country string enter information manually
+d-i mirror/http/hostname string mirrors.private
+d-i mirror/http/directory string /debian
+d-i mirror/http/proxy string
+
+# Suite to install.
+d-i mirror/suite string lenny
+
+### Clock and time zone setup
+d-i clock-setup/utc boolean false
+d-i time/zone string Europe/Paris
+d-i clock-setup/ntp boolean false
+d-i clock-setup/ntp-server string fwadmin.vlan-systeme.private
+
+### Partitioning
+d-i partman-auto/disk string /dev/sda
+d-i partman-auto/method string regular
+d-i partman-auto/purge_lvm_from_device boolean true
+d-i partman-lvm/confirm boolean true
+#d-i partman-auto/choose_recipe \
+#       select All files in one partition (recommended for new users)
+d-i partman-auto/expert_recipe string                       \
+      boot-root ::                                          \
+              64 72 80 ext3                                 \
+                     $primary{ } $bootable{ }               \
+                     method{ format } format{ }             \
+                     use_filesystem{ } filesystem{ ext3 }   \
+                     mountpoint{ /boot }                    \
+              .                                             \
+              500 10000 1000000000 ext3                     \
+	      	     $primary{ }			    \
+                     method{ format } format{ }             \
+                     use_filesystem{ } filesystem{ ext3 }   \
+                     mountpoint{ / }                        \
+              .                                             \
+              64 512 100% linux-swap                        \
+                     method{ swap } format{ }               \
+              .
+
+d-i partman/confirm_write_new_label boolean true
+d-i partman/choose_partition \
+       select Finish partitioning and write changes to disk
+d-i partman/confirm boolean true
+
+### Base system installation
+d-i base-installer/kernel/image string linux-image-2.6.26.5-universal-grm2.1.12-grsec
+
+### Account setup
+d-i passwd/root-login boolean false
+d-i passwd/user-fullname string Auto Installer
+d-i passwd/username string auto
+d-i passwd/user-password password auto
+d-i passwd/user-password-again password auto
+#d-i passwd/user-password-crypted password [MD5 hash]
+
+### Apt setup
+d-i apt-setup/non-free boolean true
+d-i apt-setup/contrib boolean true
+d-i apt-setup/services-select string multi-select security
+d-i apt-setup/security_host string mirrors.private/debian-security
+#d-i apt-setup/volatile_host string volatile.debian.org
+# Additional repositories, local[0-9] available
+d-i apt-setup/local0/repository string \
+       http://mirrors.private/debian-custom lenny-custom common
+#d-i apt-setup/local0/key string http://local.server/key
+# By default the installer requires that repositories be authenticated
+# using a known gpg key. This setting can be used to disable that
+# authentication. Warning: Insecure, not recommended.
+d-i debian-installer/allow_unauthenticated string true
+
+### Package selection
+tasksel tasksel/first string multiselect standard
+# Individual additional packages to install
+#d-i pkgsel/include string openssh-server
+#d-i pkgsel/include string pf-tools
+popularity-contest popularity-contest/participate boolean false
+
+### Boot loader installation
+# Grub is the default boot loader (for x86). If you want lilo installed
+# instead, uncomment this:
+#d-i grub-installer/skip boolean true
+# To also skip installing lilo, and install no bootloader, uncomment this
+# too:
+#d-i lilo-installer/skip boolean true
+# This is fairly safe to set, it makes grub install automatically to the MBR
+# if no other operating system is detected on the machine.
+#d-i grub-installer/only_debian boolean true
+# This one makes grub-installer install to the MBR if it also finds some other
+# OS, which is less safe as it might not be able to boot that other OS.
+#d-i grub-installer/with_other_os boolean false
+# Alternatively, if you want to install to a location other than the mbr,
+# uncomment and edit these lines:
+#d-i grub-installer/only_debian boolean false
+#d-i grub-installer/with_other_os boolean false
+#d-i grub-installer/bootdev  string (hd0,0)
+# To install grub to multiple disks:
+#d-i grub-installer/bootdev  string (hd0,0) (hd1,0) (hd2,0)
+
+### Post-install command before reboot
+d-i preseed/late_command string apt-install linux-image-2.6.26.5-universal-grm2.1.12-grsec ; apt-install nfs-common ; apt-install pf-tools ; in-target wget http://mirrors.private//path/to/some/script -O /tmp//path/to/some/script ; in-target sh /tmp//path/to/some/script 
+
+### Finishing up the installation
+# Avoid that last message about the install being complete.
+d-i finish-install/reboot_in_progress note
+
+#### Advanced options
+### Running custom commands during the installation
+# d-i preseeding is inherently not secure. Nothing in the installer checks
+# for attempts at buffer overflows or other exploits of the values of a
+# preconfiguration file like this one. Only use preconfiguration files from
+# trusted locations! To drive that home, and because it's generally useful,
+# here's a way to run any shell command you'd like inside the installer,
+# automatically.
+
+# This first command is run as early as possible, just after
+# preseeding is read.
+#d-i preseed/early_command string anna-install some-udeb
+
+# This command is run just before the install finishes, but when there is
+# still a usable /target directory. You can chroot to /target and use it
+# directly, or use the apt-install and in-target commands to easily install
+# packages and run commands in the target system.
+#d-i preseed/late_command string apt-install zsh; in-target chsh -s /bin/zsh
+#d-i preseed/late_command string apt-install pf-tools; in-target update-config
+
+EOT
+
+$test_output_file = q{/tmp/pf-test/var/www/preseed/preseed_cbv4-rdeploy01};
+
+my $host_ref = get_host_config( $hostname, $global_config, $site_name );
+
+my $preseed_file = make_preseed_file(
+    {
+        hostname                  => $hostname,
+        host_ref                  => $host_ref,
+        site_name                 => $site_name,
+        preseed_template_filename => q{templates/standard-preseed},
+        pf_config                 => $pf_config,
+        pf_script                 => q{/path/to/some/script},
+    }
+);
+is $preseed_file, basename($test_output_file)
+    => q{Returns the correct preseed filename};
+
+$result = PFTools::Utils::__read_file_in_array( $test_output_file );
+
+is_deeply $result, $expected_result
+    => q{Returns the expected preseed content for host cbv4-rdeploy01 site cbv4}
+    or note explain $result;
+
+########################################################################
+note('Testing PFTools::Utils::make_pxe_boot_and_preseed_files');
+can_ok( 'PFTools::Utils', qw( make_pxe_boot_and_preseed_files ) );
+
+ok make_pxe_boot_and_preseed_files(
+    {
+        hostname                  => $hostname,
+        site_name                 => $site_name,
+        global_config             => $global_config,
+        pf_config                 => $pf_config,
+        pf_script                 => q{/path/to/some/script},
+        preseed_template_filename => q{templates/standard-preseed},
+        pxe_template_filename     => q{templates/standard-installer},
+    }
+    )
+    => q{Returns success};
+
+$result = PFTools::Utils::__read_file_in_array( $test_output_file );
+is_deeply $result, $expected_result
+    => q{Returns the expected preseed result for host cbv4-rdeploy01 site cbv4}
+    or note explain $result;
+
+$expected_result = [ split qr{ \n }xms, <<"EOT", -1 ];
+SERIAL 0 115200 2
+DISPLAY debian-installer/lenny/amd64/boot-screens/myboot.txt
+
+DEFAULT linux
+
+LABEL install
+\tkernel debian-installer/lenny/amd64/linux
+\tappend DEBCONF_PRIORITY=critical vga=normal auto=true initrd=debian-installer/lenny/amd64/initrd.gz interface=eth0 netcfg/no_default_route=true url=http://vip-deploy.vlan-systeme.private/preseed_cbv4-rdeploy01 url/checksum=aa03e0e1599f6da3149aa94027d116a0 -- default 
+
+LABEL linux
+\tkernel vmlinuz-2.6.26.5-universal-grm2.1.12
+\tappend vga=normal root=/dev/sda2 -- default 
+
+PROMPT 1
+TIMEOUT 100
+
+EOT
+
+$result = PFTools::Utils::__read_file_in_array(
+    q{/tmp/pf-test/distrib/tftpboot/pxelinux.cfg/00-1e-c9-ff-42-0b}
+);
+is_deeply $result, $expected_result
+    => q{Builds the expected pxe_boot_file content for host cbv4-rdeploy01 site cbv4'}
+    or note explain $result;
+
+########################################################################
+note('Testing PFTools::Utils::__fix_etc_hosts');
+can_ok( 'PFTools::Utils', qw( __fix_etc_hosts ) );
+
+throws_ok { PFTools::Utils::__fix_etc_hosts(); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] ip_type }xms
+    => q{Dies if empty iptype};
+
+throws_ok { PFTools::Utils::__fix_etc_hosts( { ip_type => {} } ); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] ip_type }xms
+    => q{Dies if non-scalar iptype};
+
+$args_ref = {
+    ip_type       => q{ipv6},
+};
+throws_ok { PFTools::Utils::__fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] __fix_etc_hosts: [ ] ip_type [ ] 'ipv6' [ ] not [ ] implemented [ ] }xms
+    => q{Dies if iptype eq 'ipv6'};
+
+$args_ref = {
+    ip_type       => q{ipv4},
+};
+throws_ok { PFTools::Utils::__fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] hostname }xms
+    => q{Dies if empty hostname};
+
+$args_ref->{'hostname'} = {};
+throws_ok { PFTools::Utils::__fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] hostname }xms
+    => q{Dies if non-scalar hostname};
+
+$args_ref->{'hostname'} = q{cbv4-rdeploy01};
+throws_ok { PFTools::Utils::__fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] global_config }xms
+    => q{Dies if empty global_config};
+
+$args_ref->{'global_config'} = q{global_config};
+throws_ok { PFTools::Utils::__fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] global_config }xms
+    => q{Dies if non-hashref global_config};
+
+$args_ref->{'global_config'} = $global_config;
+throws_ok { PFTools::Utils::__fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] filename }xms
+    => q{Dies if empty input_filename};
+
+$args_ref->{'input_filename'} = {};
+throws_ok { PFTools::Utils::__fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] filename }xms
+    => q{Dies if non-scalar input_filename};
+
+my $input_filename = q{t/20.files.etc.hosts.input};
+$args_ref->{'input_filename'} = $input_filename;
+
+# site_name is optional
+
+$result = PFTools::Utils::__fix_etc_hosts($args_ref);
+$expected_result = [qq{10.1.167.1\tcbv4-rdeploy01}];
+
+is_deeply $result, $expected_result
+    => q{Correctly fixes /etc/hosts content for host cbv4-rdeploy01 site cbv4'}
+    or note explain $result;
+
+########################################################################
+note('Testing PFTools::Utils::fix_etc_hosts');
+can_ok( 'PFTools::Utils', qw( fix_etc_hosts ) );
+
+my $output_filename = q{t/20.files.etc.hosts.output};
+
+$args_ref = {
+    hostname      => q{cbv4-rdeploy01},
+    site_name     => q{cbv4},
+    ip_type       => q{ipv4},
+    global_config => $global_config,
+    pf_config     => $pf_config,
+};
+
+$args_ref->{'input_filename'} = undef;
+$args_ref->{'output_filename'} = undef;
+throws_ok { fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] input_filename }xms
+    => q{Dies if undefined input_filename};
+
+$args_ref->{'input_filename'} = {};
+throws_ok { fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] input_filename }xms
+    => q{Dies if non-scalar input_filename};
+
+$args_ref->{'input_filename'} = $input_filename;
+
+throws_ok { fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] empty [ ] output_filename }xms
+    => q{Dies if empty output_filename};
+
+$args_ref->{'output_filename'} = {};
+throws_ok { fix_etc_hosts($args_ref); }
+qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] output_filename }xms
+    => q{Dies if non-scalar output_filename};
+
+$args_ref->{'output_filename'} = $output_filename;
+
+ok fix_etc_hosts($args_ref)
+    => q{Returns a true value};
+
+$result = PFTools::Utils::__read_file_in_array($output_filename, 1);
+is_deeply $result, $expected_result
+    => q{Correctly fixes /etc/hosts file for host cbv4-rdeploy01 site cbv4'}
+    or note explain $result;
+
+ok unlink $output_filename
+    => q{Removed test output file};
+
+ok copy($input_filename, $output_filename)
+   => q{Copied the test input file for the next test};
+
+$args_ref->{'input_filename'} = $output_filename;
+
+ok fix_etc_hosts($args_ref)
+    => q{Returns a true value};
+
+$result = PFTools::Utils::__read_file_in_array($output_filename, 1);
+is_deeply $result, $expected_result
+    => q{Correctly fixes /etc/hosts file for host cbv4-rdeploy01 site cbv4,
+    even if input and output are the same file'}
+    or note explain $result;
+
+ok unlink $output_filename
+    => q{Removed test output file};
+
diff -r 9e79004112bc -r de2d381543f5 t/20.zone.t
--- a/t/20.zone.t	Wed Nov 24 19:39:32 2010 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,814 +0,0 @@
-#!perl
-
-use strict;
-use warnings;
-
-use Cwd;
-use English qw( -no_match_vars );    # Avoids regex performance penalty
-use Sys::Hostname;
-use Test::Exception;
-use Test::More qw( no_plan );
-
-use PFTools::Conf;
-use PFTools::Utils qw( /^./ ); # import @EXPORT and @EXPORT_OK
-
-# Let's go back to our test configuration
-# FIXME: this depends on t/13.conf.t, but it probably should not.
-my $config_file = 't/13.conf.cfg1/etc/pf-tools.1.conf';
-my $pf_config = Init_PF_CONFIG($config_file);    # already tested OK in 13.conf.t
-
-my $hostname = hostname;
-my $hash_subst = Init_SUBST( $hostname, undef, $pf_config, 'private' );
-
-my $global_config = Init_GLOBAL_NETCONFIG( q{COMMON:private-network}, $hash_subst );
-
-########################################################################
-note('Testing PFTools::Utils::__make_zone_header');
-can_ok( 'PFTools::Utils', qw( __make_zone_header ) );
-
-throws_ok { PFTools::Utils::__make_zone_header(); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] zone_name }xms
-    => q{Dies if empty $zone_name};
-
-throws_ok { PFTools::Utils::__make_zone_header( {} ); }
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] zone_name }xms
-    => q{Dies if non-scalar $zone_name};
-
-throws_ok { PFTools::Utils::__make_zone_header( q{name} ); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] site_name }xms
-    => q{Dies if empty $site_name};
-
-throws_ok { PFTools::Utils::__make_zone_header( q{name}, {} ); }
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site_name }xms
-    => q{Dies if non-scalar $site_name};
-
-throws_ok { PFTools::Utils::__make_zone_header( q{name}, q{site} ); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] zone_ref }xms
-    => q{Dies if empty $zone_ref};
-
-throws_ok { PFTools::Utils::__make_zone_header( q{name}, q{site}, q{ref} ); }
-qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] zone_ref }xms
-    => q{Dies if non-hashref $zone_ref};
-
-throws_ok { PFTools::Utils::__make_zone_header( q{name}, q{site}, {} ); }
-qr{ \A ERROR: [ ] Invalid [ ] [\$] zone_ref [ ] hashref:
-    [ ] no [ ] 'SOA' [ ] key [ ] found }xms
-    => q{Dies if non-zone hashref $zone_ref};
-
-
-my $zone_ref = $global_config->{'ZONE'}->{'BY_NAME'}->{'private'};
-$zone_ref->{'SOA'}->{'serial'} = '1289575205';
-my $result = PFTools::Utils::__make_zone_header( 'private', 'cbv4-pfds', $zone_ref );
-my $expected_result = [
-    ';;',
-    ';; BIND configuration file for zone: private',
-    ';; Site: cbv4-pfds',
-    ';;',
-    ';; Internal management zone',
-    ';;============================================================================',
-    '',
-    '$TTL 1D      ; TTL (1 day)',
-    '@                             IN SOA'     . qq{\t} . 'Deploy00.private. dnsmaster at private (',
-    '                              1289575205' . qq{\t} . '; Serial',
-    '                              6H      ; Refresh (6 hours)',
-    '                              1H      ; Retry (1 hour)',
-    '                              7D      ; Expire (7 days)',
-    '                              1H      ; Negative TTL (1 hours)',
-    '                              )',
-    '',
-    '                              IN NS' . qq{\t} . 'deploy00.vlan-systeme.private.',
-    '                              IN NS' . qq{\t} . 'deploy01.vlan-systeme.private.',
-    '',
-    '                              IN MX' . qq{\t} . '1       mf.private.',
-    '                              IN MX' . qq{\t} . '2       mf00.private.',
-    '                              IN MX' . qq{\t} . '2       mf01.private.',
-    '',
-    '',
-];
-
-is_deeply $result, $expected_result
-    => q{Returns the expected result for zone 'private'}
-    or note explain $result;
-
-
-########################################################################
-note('Testing PFTools::Utils::__build_zone');
-can_ok( 'PFTools::Utils', qw( __build_zone ) );
-
-throws_ok { PFTools::Utils::__build_zone(); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] zone_name }xms
-    => q{Dies if empty $zone_name};
-
-throws_ok { PFTools::Utils::__build_zone( { zone_name => {} } ); }
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] zone_name }xms
-    => q{Dies if non-scalar $zone_name};
-
-throws_ok { PFTools::Utils::__build_zone( { zone_name => q{name} } ); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] site_name }xms
-    => q{Dies if empty $site_name};
-
-throws_ok {
-    PFTools::Utils::__build_zone(
-        {
-            zone_name => q{name},
-            site_name => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site_name }xms
-    => q{Dies if non-scalar $site_name};
-
-throws_ok {
-    PFTools::Utils::__build_zone(
-        {
-            zone_name => q{name},
-            site_name => q{site},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] global_config }xms
-    => q{Dies if empty $global_config};
-
-throws_ok {
-    PFTools::Utils::__build_zone(
-        {
-            zone_name     => q{name},
-            site_name     => q{site},
-            global_config => q{ref},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
-    => q{Dies if non-hashref $global_config};
-
-throws_ok {
-    PFTools::Utils::__build_zone(
-        {
-            zone_name     => q{name},
-            site_name     => q{site},
-            global_config => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] [\$] global_config [ ] hashref:
-    [ ] no [ ] 'ZONE' [ ] key [ ] found }xms
-    => q{Dies if non-config hashref $global_config};
-
-throws_ok {
-    PFTools::Utils::__build_zone(
-        {
-            zone_name     => q{name},
-            site_name     => q{site},
-            global_config => $global_config,
-        }
-    );
-}
-qr{ \A ERROR: [ ] Unknown [ ] zone_name: [ ] }xms
-    => q{Dies if unknown zone_name};
-
-throws_ok {
-    PFTools::Utils::__build_zone(
-        {
-            zone_name     => q{private},
-            site_name     => q{site},
-            global_config => $global_config
-        }
-    );
-}
-qr{ \A ERROR: [ ] Unknown [ ] site_name: [ ] }xms
-    => q{Dies if unknown site_name};
-
-$result = PFTools::Utils::__build_zone(
-    {
-        zone_name     => q{private},
-        site_name     => q{cbv4-pfds},
-        global_config => $global_config,
-    }
-);
-$expected_result = [
-    ';;',
-    ';; BIND configuration file for zone: private',
-    ';; Site: cbv4-pfds',
-    ';;',
-    ';; Internal management zone',
-    ';;============================================================================',
-    '',
-    '$TTL 1D      ; TTL (1 day)',
-    '@                             IN SOA	Deploy00.private. dnsmaster at private (',
-    '                              1289575205	; Serial',
-    '                              6H      ; Refresh (6 hours)',
-    '                              1H      ; Retry (1 hour)',
-    '                              7D      ; Expire (7 days)',
-    '                              1H      ; Negative TTL (1 hours)',
-    '                              )',
-    '',
-    '                              IN NS	deploy00.vlan-systeme.private.',
-    '                              IN NS	deploy01.vlan-systeme.private.',
-    '',
-    '                              IN MX	1       mf.private.',
-    '                              IN MX	2       mf00.private.',
-    '                              IN MX	2       mf01.private.',
-    '',
-    '',
-    ';;',
-    ';; Networks',
-    ';;============================================================================',
-    '',
-    '; vlan-systeme',
-    ';----------------------------------------------------------------------------',
-    'network.vlan-systeme          IN A	10.1.0.0',
-    'netmask.vlan-systeme          IN A	255.255.0.0',
-    'broadcast.vlan-systeme        IN A	10.1.255.255',
-    '',
-    '; vlan-pfds-int',
-    ';----------------------------------------------------------------------------',
-    'network.vlan-pfds-int         IN A	10.2.0.0',
-    'netmask.vlan-pfds-int         IN A	255.255.0.0',
-    'broadcast.vlan-pfds-int       IN A	10.2.255.255',
-    '',
-    '; vlan-admindsi',
-    ';----------------------------------------------------------------------------',
-    'network.vlan-admindsi         IN A	10.3.1.0',
-    'netmask.vlan-admindsi         IN A	255.255.255.0',
-    'broadcast.vlan-admindsi       IN A	10.3.1.255',
-    'gateway.vlan-admindsi         IN A	10.3.1.254',
-    '',
-    '; vlan-middledsi',
-    ';----------------------------------------------------------------------------',
-    'network.vlan-middledsi        IN A	10.3.2.0',
-    'netmask.vlan-middledsi        IN A	255.255.255.0',
-    'broadcast.vlan-middledsi      IN A	10.3.2.255',
-    'gateway.vlan-middledsi        IN A	10.3.2.254',
-    '',
-    '; vlan-pfds-ext',
-    ';----------------------------------------------------------------------------',
-    'network.vlan-pfds-ext         IN A	192.168.1.0',
-    'netmask.vlan-pfds-ext         IN A	255.255.255.0',
-    'broadcast.vlan-pfds-ext       IN A	192.168.1.255',
-    'gateway.vlan-pfds-ext         IN A	192.168.1.254',
-    '',
-    '',
-    '',
-    ';;',
-    ';; Servers',
-    ';;============================================================================',
-    '',
-    '; vip-spawn',
-    ';----------------------------------------------------------------------------',
-    'cvs                           IN CNAME	vip-spawn.vlan-systeme',
-    'mf                            IN CNAME	vip-spawn.vlan-systeme',
-    'mirrors                       IN CNAME	vip-spawn.vlan-systeme',
-    'nscache                       IN CNAME	vip-spawn.vlan-systeme',
-    'nsprivate                     IN CNAME	vip-spawn.vlan-systeme',
-    'vip-deploy                    IN CNAME	vip-spawn.vlan-systeme',
-    'vip-spawn.vlan-pfds-ext       IN A	192.168.1.99',
-    'vip-spawn.vlan-systeme        IN A	10.1.1.254',
-    '',
-    '; cbv4-pfds-filer',
-    ';----------------------------------------------------------------------------',
-    'cbv4-pfds-filer00.vlan-systeme IN A	10.1.2.0',
-    'cbv4-pfds-filer01.vlan-systeme IN A	10.1.2.1',
-    '',
-    '; cbv4-spawn',
-    ';----------------------------------------------------------------------------',
-    'cbv4-spawn                    IN CNAME	cbv4-spawn.vlan-systeme',
-    'cbv4-spawn.vlan-systeme       IN A	10.1.167.0',
-    'cbv4-spawn.vlan-systeme       IN A	10.1.167.1',
-    'cbv4-spawn00.vlan-admindsi    IN A	10.3.1.41',
-    'cbv4-spawn00.vlan-middledsi   IN A	10.3.2.41',
-    'cbv4-spawn00.vlan-pfds-ext    IN A	192.168.1.97',
-    'cbv4-spawn00.vlan-pfds-int    IN A	10.2.167.0',
-    'cbv4-spawn00.vlan-systeme     IN A	10.1.167.0',
-    'cbv4-spawn01.vlan-admindsi    IN A	10.3.1.42',
-    'cbv4-spawn01.vlan-middledsi   IN A	10.3.2.42',
-    'cbv4-spawn01.vlan-pfds-ext    IN A	192.168.1.98',
-    'cbv4-spawn01.vlan-pfds-int    IN A	10.2.167.1',
-    'cbv4-spawn01.vlan-systeme     IN A	10.1.167.1',
-    'ntp                           IN CNAME	cbv4-spawn.vlan-systeme',
-    'ntp00                         IN CNAME	cbv4-spawn00.vlan-systeme',
-    'ntp01                         IN CNAME	cbv4-spawn01.vlan-systeme',
-    'spawn                         IN CNAME	cbv4-spawn.vlan-systeme',
-    'spawn00                       IN CNAME	cbv4-spawn00.vlan-systeme',
-    'spawn01                       IN CNAME	cbv4-spawn01.vlan-systeme',
-    ''
-];
-
-is_deeply $result, $expected_result
-    => q{Returns the expected result for zone 'private'}
-    or note explain $result;
-
-
-########################################################################
-note('Testing PFTools::Utils::make_zone_file');
-can_ok( 'PFTools::Utils', qw( make_zone_file ) );
-
-throws_ok { make_zone_file(); }
-qr{ \A ERROR: [ ] Invalid [ ] [\$] arguments_ref }xms
-    => q{Dies if no args};
-
-my $test_output_file = q{test.zone};
-$result = make_zone_file(
-    {
-        zone_name     => q{private},
-        site_name     => q{cbv4-pfds},
-        filename      => $test_output_file,
-        global_config => $global_config,
-    }
-);
-ok $result => q{Returns true on success};
-
-$result = PFTools::Utils::__read_file_in_array( $test_output_file, 1 );
-
-is_deeply $result, $expected_result
-    => q{Returns the expected result for host cbv4-rdeploy01 site cbv4'}
-    or note explain $result;
-
-ok unlink($test_output_file)
-    => q{Removed the test-generated zone file};
-
-
-########################################################################
-note('Testing PFTools::Utils::make_resolv_conf_file');
-can_ok( 'PFTools::Utils', qw( make_resolv_conf_file ) );
-
-throws_ok { make_resolv_conf_file(); }
-qr{ \A ERROR: [ ] Invalid [ ] [\$] arguments_ref }xms
-    => q{Dies if no args};
-
-throws_ok { make_resolv_conf_file( {} ); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] filename }xms
-    => q{Dies if empty $filename};
-
-throws_ok { make_resolv_conf_file( { filename => {} } ); }
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] filename }xms
-    => q{Dies if non-scalar $filename};
-
-throws_ok { make_resolv_conf_file( { filename => q{filename} } ); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] hostname }xms
-    => q{Dies if empty $hostname};
-
-throws_ok {
-    make_resolv_conf_file(
-        {
-            filename => q{filename},
-            hostname => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] hostname }xms
-    => q{Dies if non-scalar $hostname};
-
-throws_ok {
-    make_resolv_conf_file(
-        {
-            filename => q{filename},
-            hostname => q{name},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] global_config}xms
-    => q{Dies if empty $global_config};
-
-throws_ok {
-    make_resolv_conf_file(
-        {
-            filename      => q{filename},
-            hostname      => q{name},
-            global_config => q{global_config},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config [ ]}xms
-    => q{Dies if non-hashref $global_config};
-
-throws_ok {
-    make_resolv_conf_file(
-        {
-            filename      => q{filename},
-            hostname      => q{name},
-            global_config => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] [\$] global_config [ ] hashref:
-    [ ] no [ ] 'ZONE' [ ] key [ ] found }xms
-    => q{Dies if non-config hashref $global_config};
-
-throws_ok {
-    make_resolv_conf_file(
-        {
-            filename      => q{filename},
-            hostname      => q{hostname},
-            global_config => $global_config,
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] site_name }xms
-    => q{Dies if empty $site_name};
-
-throws_ok {
-    make_resolv_conf_file(
-        {
-            filename      => q{filename},
-            hostname      => q{name},
-            global_config => $global_config,
-            site_name     => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site_name }xms
-    => q{Dies if non-scalar $site_name};
-
-$test_output_file = q{test.resolv.conf};
-$result           = make_resolv_conf_file(
-    {
-        hostname      => q{cbv4-rdeploy01},
-        global_config => $global_config,
-        site_name     => q{cbv4},
-        filename      => $test_output_file,
-    }
-);
-ok $result => q{Returns true on success};
-
-$result = PFTools::Utils::__read_file_in_array( $test_output_file, 1 );
-$expected_result = [
-    q{#},
-    q{# This file was auto-generated by mk_resolvconf -- DO NOT EDIT!},
-    q{#},
-    q{},
-    q{search private},
-    q{},
-    q{nameserver 10.1.167.0},
-    q{nameserver 10.1.167.1},
-    q{},
-];
-
-is_deeply $result, $expected_result
-    => q{Returns the expected result for host cbv4-rdeploy01 site cbv4'}
-    or note explain $result;
-
-ok unlink($test_output_file)
-    => q{Removed the test-generated resolv.conf file};
-
-
-########################################################################
-note('Testing PFTools::Utils::__build_interfaces');
-can_ok( 'PFTools::Utils', qw( __build_interfaces ) );
-
-throws_ok { PFTools::Utils::__build_interfaces(); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] hostname }xms
-    => q{Dies if empty $hostname};
-
-throws_ok { PFTools::Utils::__build_interfaces( { hostname => {} } ); }
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] hostname }xms
-    => q{Dies if non-scalar $hostname};
-
-throws_ok {
-    PFTools::Utils::__build_interfaces( { hostname => q{hostname} } );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] global_config }xms
-    => q{Dies if empty $global_config};
-
-throws_ok {
-    PFTools::Utils::__build_interfaces(
-        {
-            hostname      => q{hostname},
-            global_config => q{global_config},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
-    => q{Dies if non-hashref $global_config};
-
-throws_ok {
-    PFTools::Utils::__build_interfaces(
-        {
-            hostname      => q{name},
-            global_config => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] [\$] global_config [ ] hashref:
-    [ ] no [ ] 'ZONE' [ ] key [ ] found }xms
-    => q{Dies if non-config hashref $global_config};
-
-throws_ok {
-    PFTools::Utils::__build_interfaces(
-        {
-            hostname      => q{hostname},
-            global_config => $global_config,
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] pf_config }xms
-    => q{Dies if empty $pf_config};
-
-throws_ok {
-    PFTools::Utils::__build_interfaces(
-        {
-            hostname      => q{hostname},
-            global_config => $global_config,
-            pf_config     => q{pf_config},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] pf_config }xms
-    => q{Dies if non-hashref $pf_config};
-
-throws_ok {
-    PFTools::Utils::__build_interfaces(
-        {
-            hostname      => q{hostname},
-            global_config => $global_config,
-            pf_config     => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] [\$] pf_config [ ] hashref:
-    [ ] no [ ] 'vcs' [ ] key [ ] found }xms
-    => q{Dies if non-config hashref $pf_config};
-
-throws_ok {
-    PFTools::Utils::__build_interfaces(
-        {
-            hostname      => q{hostname},
-            global_config => $global_config,
-            pf_config     => $pf_config,
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] site_name }xms
-    => q{Dies if empty $site_name};
-
-throws_ok {
-    PFTools::Utils::__build_interfaces(
-        {
-            hostname      => q{hostname},
-            global_config => $global_config,
-            pf_config     => $pf_config,
-            site_name     => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site_name }xms
-    => q{Dies if non-scalar $site_name};
-
-$result = PFTools::Utils::__build_interfaces(
-    {
-        hostname      => q{cbv4-spawn01},
-        global_config => $global_config,
-        pf_config     => $pf_config,
-        site_name     => q{cbv4-pfds},
-    }
-);
-$expected_result = [
-    qq{#},
-    qq{# This file was auto-generated by mk_interfaces -- DO NOT EDIT!},
-    qq{#},
-    qq{},
-    qq{auto lo},
-    qq{iface lo inet loopback},
-    qq{},
-    qq{auto bond0},
-    qq{iface bond0 inet static},
-    qq{\tslaves\t\teth2 eth3},
-    qq{\taddress\t\t192.168.1.98},
-    qq{\tnetmask\t\t255.255.255.0},
-    qq{\tnetwork\t\t192.168.1.0},
-    qq{\tbroadcast\t192.168.1.255},
-    qq{},
-    qq{auto eth0},
-    qq{iface eth0 inet static},
-    qq{\taddress\t\t10.1.167.1},
-    qq{\tnetmask\t\t255.255.0.0},
-    qq{\tnetwork\t\t10.1.0.0},
-    qq{\tbroadcast\t10.1.255.255},
-    qq{},
-    qq{auto eth0.39},
-    qq{iface eth0.39 inet static},
-    qq{\taddress\t\t10.2.167.1},
-    qq{\tnetmask\t\t255.255.0.0},
-    qq{\tnetwork\t\t10.2.0.0},
-    qq{\tbroadcast\t10.2.255.255},
-    qq{\tvlan_raw_device\teth0},
-    qq{\tup\t\t/sbin/ip link set eth0.39 mtu 1496},
-    qq{},
-    qq{auto eth4},
-    qq{iface eth4 inet static},
-    qq{\taddress\t\t10.3.1.42},
-    qq{\tnetmask\t\t255.255.255.0},
-    qq{\tnetwork\t\t10.3.1.0},
-    qq{\tbroadcast\t10.3.1.255},
-    qq{},
-    qq{auto eth5},
-    qq{iface eth5 inet static},
-    qq{\taddress\t\t10.3.2.42},
-    qq{\tnetmask\t\t255.255.255.0},
-    qq{\tnetwork\t\t10.3.2.0},
-    qq{\tbroadcast\t10.3.2.255},
-    qq{},
-];
-
-is_deeply $result, $expected_result
-    => q{Returns the expected result for host cbv4-spawn01 site cbv4-pfds'}
-    or note explain $result;
-
-########################################################################
-note('Testing PFTools::Utils::make_interfaces_file');
-can_ok( 'PFTools::Utils', qw( make_interfaces_file ) );
-
-throws_ok { make_interfaces_file(); }
-qr{ \A ERROR: [ ] Invalid [ ] [\$] arguments_ref }xms
-    => q{Dies if no args};
-
-$test_output_file = q{test.interfaces};
-$result = make_interfaces_file(
-    {
-        hostname      => q{cbv4-spawn01},
-        global_config => $global_config,
-        pf_config     => $pf_config,
-        site_name     => q{cbv4-pfds},
-        filename      => $test_output_file,
-    }
-);
-ok $result => q{Returns true on success};
-
-$result = PFTools::Utils::__read_file_in_array( $test_output_file, 1 );
-
-is_deeply $result, $expected_result
-    => q{Returns the expected result for host cbv4-spawn01 site cbv4-pfds'}
-    or note explain $result;
-
-ok unlink($test_output_file)
-    => q{Removed the test-generated interfaces file};
-
-########################################################################
-note('Testing PFTools::Utils::__build_sources_list');
-can_ok( 'PFTools::Utils', qw( __build_sources_list ) );
-
-throws_ok { PFTools::Utils::__build_sources_list(); }
-qr{ \A ERROR: }xms
-    => q{Dies if no $arguments_ref};
-
-throws_ok { PFTools::Utils::__build_sources_list( {} ); }
-qr{ \A ERROR: }xms
-    => q{Dies if empty $arguments_ref};
-
-throws_ok { PFTools::Utils::__build_sources_list( { template => q{} } ); }
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] template }xms
-    => q{Dies if empty $template};
-
-throws_ok { PFTools::Utils::__build_sources_list( { template => {} } ); }
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] template }xms
-    => q{Dies if non-scalar $template};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        { template => q{template}, hostname => q{}, sections_ref => q{} }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] sections_ref }xms
-    => q{Dies if empty $sections_ref};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        { template => q{template}, hostname => q{}, sections_ref => {} }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-array-ref [ ] [\$] sections_ref }xms
-    => q{Dies if non-array-ref $sections_ref};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        { template => q{template}, hostname => q{}, sections_ref => [] }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty-array-ref [ ] [\$] sections_ref }xms
-    => q{Dies if empty-array-ref $sections_ref};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        { template => q{template}, hostname => q{}, sections_ref => [q{}] }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] or [ ] blank [ ] section [ ] in [ ] array-ref [ ] [\$] sections_ref }xms
-    => q{Dies if empty section in array-ref $sections_ref};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        { template => q{template}, hostname => q{}, sections_ref => [q{ }] }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] or [ ] blank [ ] section [ ] in [ ] array-ref [ ] [\$] sections_ref }xms
-    => q{Dies if blank section in array-ref $sections_ref};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        {
-            template     => q{template},
-            sections_ref => [q{section}],
-            backports    => {}
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] backports }xms
-    => q{Dies if non-scalar $backports};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        {
-            template      => q{template},
-            sections_ref  => [q{section}],
-            hostname      => q{hostname},
-            global_config => q{foo},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
-    => q{Dies if non-hashref $global_config};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        {
-            template      => q{template},
-            sections_ref  => [q{section}],
-            hostname      => q{hostname},
-            global_config => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] [\$] global_config [ ] hashref: }xms
-    => q{Dies if non-config hashref $global_config};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        {
-            template      => q{template},
-            sections_ref  => [q{section}],
-            hostname      => q{hostname},
-            global_config => $global_config,
-            hostname      => q{},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] empty [ ] [\$] hostname }xms
-    => q{Dies if empty $hostname};
-
-throws_ok {
-    PFTools::Utils::__build_sources_list(
-        {
-            template      => q{template},
-            sections_ref  => [q{section}],
-            hostname      => q{hostname},
-            global_config => $global_config,
-            hostname      => {},
-        }
-    );
-}
-qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] hostname }xms
-    => q{Dies if non-scalar $hostname};
-
-my $test_sections_ref = [qw(common uncommon)];
-
-$expected_result = <<'EOT';
-#
-# Generated by mk_sourceslist -- DO NOT EDIT!
-#
-
-deb http://mirrors.private/debian lenny main contrib non-free
-deb http://mirrors.private/debian-custom lenny-custom common uncommon
-deb http://mirrors.private/debian-security lenny/updates main contrib non-free
-
-EOT
-
-########################################################################
-note('Testing PFTools::Utils::make_sources_list_file');
-can_ok( 'PFTools::Utils', qw( make_sources_list_file ) );
-
-throws_ok { make_sources_list_file(); }
-qr{ \A ERROR: [ ] Invalid [ ] [\$] arguments_ref }xms
-    => q{Dies if no args};
-
-$test_output_file = q{test.sources.list};
-$result = make_sources_list_file(
-    {
-        hostname      => q{cbv4-rdeploy01},
-        site_name     => q{cbv4},
-        filename      => $test_output_file,
-        sections_ref  => $test_sections_ref,
-        template      => q{templates/sources.list},
-        global_config => $global_config,
-        pf_config     => $pf_config,
-    }
-);
-ok $result => q{Returns true on success};
-
-$result = PFTools::Utils::__read_file_in_scalar( $test_output_file );
-
-is_deeply $result, $expected_result
-    => q{Returns the expected result for host cbv4-rdeploy01 site cbv4'}
-    or note explain $result;
-
-ok unlink($test_output_file)
-    => q{Removed the test-generated interfaces file};
-
diff -r 9e79004112bc -r de2d381543f5 templates/sources.list
--- a/templates/sources.list	Wed Nov 24 19:39:32 2010 +0100
+++ b/templates/sources.list	Wed Dec 01 08:25:18 2010 +0100
@@ -6,3 +6,7 @@
 deb http://mirrors.private/[% mode %]-custom [% distrib %]-custom [% custom_sections %]
 deb http://mirrors.private/[% mode %]-security [% distrib %]/updates [% default_sections %]
 
+[% IF backports -%]
+deb http://mirrors.private/[% mode %]-backports [% distrib %]-backports [% default_sections %]
+
+[% END -%]
diff -r 9e79004112bc -r de2d381543f5 templates/standard-installer
--- a/templates/standard-installer	Wed Nov 24 19:39:32 2010 +0100
+++ b/templates/standard-installer	Wed Dec 01 08:25:18 2010 +0100
@@ -9,7 +9,8 @@
 
 LABEL linux
 	kernel [% kernel %]
-	append vga=normal root=/dev/sda2 initrd=[% arch %]/[% initrd %] -- [% console %] [% cmdline %]
+	append vga=normal root=/dev/sda2 [% IF initrd %]initrd=[% arch %]/[% initrd %] [% END %]-- [% console %] [% cmdline %]
 
 PROMPT 1
 TIMEOUT 100
+
diff -r 9e79004112bc -r de2d381543f5 templates/standard-preseed-squeeze.tpl
--- a/templates/standard-preseed-squeeze.tpl	Wed Nov 24 19:39:32 2010 +0100
+++ b/templates/standard-preseed-squeeze.tpl	Wed Dec 01 08:25:18 2010 +0100
@@ -215,4 +215,3 @@
 # packages and run commands in the target system.
 #d-i preseed/late_command string apt-install zsh; in-target chsh -s /bin/zsh
 
-
diff -r 9e79004112bc -r de2d381543f5 templates/ubuntu-installer
--- a/templates/ubuntu-installer	Wed Nov 24 19:39:32 2010 +0100
+++ b/templates/ubuntu-installer	Wed Dec 01 08:25:18 2010 +0100
@@ -9,7 +9,8 @@
 
 LABEL linux
 	kernel [% kernel %]
-	append vga=normal root=/dev/sda2 initrd=[% initrd %] -- [% console %] [% cmdline %]
+	append vga=normal root=/dev/sda2 [% IF initrd %]initrd=[% arch %]/[% initrd %] [% END %]-- [% console %] [% cmdline %]
 
 PROMPT 1
 TIMEOUT 100
+
diff -r 9e79004112bc -r de2d381543f5 templates/ubuntu-sources.list
--- a/templates/ubuntu-sources.list	Wed Nov 24 19:39:32 2010 +0100
+++ b/templates/ubuntu-sources.list	Wed Dec 01 08:25:18 2010 +0100
@@ -2,7 +2,10 @@
 # Except if you know what you're doing
 
 deb http://mirrors.private/[% mode %] [% distrib %] [% default_sections %]
+deb http://mirrors.private/[% mode %] [% distrib %]-security [% default_sections %]
+deb http://mirrors.private/[% mode %]-custom [% distrib %]-custom [% custom_sections %]
 
-deb http://mirrors.private/[% mode %] [% distrib %]-security [% default_sections %]
+[% IF backports -%]
+deb http://mirrors.private/[% mode %] [% distrib %]-backports [% default_sections %]
+[% END -%]
 
-deb http://mirrors.private/[% mode %]-custom [% distrib %]-custom [% custom_sections %]



More information about the pf-tools-commits mailing list