pf-tools/pf-tools: 12 new changesets

parmelan-guest at users.alioth.debian.org parmelan-guest at users.alioth.debian.org
Wed Oct 20 15:09:26 UTC 2010


details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/97881978be72
changeset: 894:97881978be72
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Fri Oct 15 18:31:45 2010 +0200
description:
Access to default and current $PF_CONFIG (and more tests).

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/a63e49f4160f
changeset: 895:a63e49f4160f
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Fri Oct 15 18:42:55 2010 +0200
description:
Remove all Get_source() in Load_conf() calls

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/6f41f34fabde
changeset: 896:6f41f34fabde
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Fri Oct 15 18:44:31 2010 +0200
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/34cf59f6c6d2
changeset: 897:34cf59f6c6d2
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Sat Oct 16 23:23:03 2010 +0200
description:
First test for Init_GLOBAL_NETCONFIG()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/76b283765f12
changeset: 898:76b283765f12
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Sat Oct 16 23:42:06 2010 +0200
description:
More tests for Init_GLOBAL_NETCONFIG()

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/d4b55376551d
changeset: 899:d4b55376551d
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Sun Oct 17 00:00:08 2010 +0200
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/6012ec3c955b
changeset: 900:6012ec3c955b
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Oct 18 15:47:29 2010 +0200
description:
Comment

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/e991a62a91db
changeset: 901:e991a62a91db
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Oct 18 16:19:46 2010 +0200
description:
Style

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/3c1d58fe98a5
changeset: 902:3c1d58fe98a5
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Oct 18 16:26:03 2010 +0200
description:
perltidy

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/57fef7a03458
changeset: 903:57fef7a03458
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Oct 18 16:34:11 2010 +0200
description:
Style / perltidy

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/d6f61d3da9f2
changeset: 904:d6f61d3da9f2
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Mon Oct 18 16:40:36 2010 +0200
description:
File::stat

details:   http://hg.debian.org/hg/pf-tools/pf-tools/rev/211fa4ce94e6
changeset: 905:211fa4ce94e6
user:      Thomas Parmelan <tom+pf-tools at ankh.fr.EU.org>
date:      Wed Oct 20 17:07:06 2010 +0200
description:
Tests for Get_hosttype_from_hostname() and Get_config_for_hostname_on_site().

Allow empty (or comments-only) configuration files in parser_ini() (with test).

diffstat:

7 files changed, 42 insertions(+), 22 deletions(-)
debian/changelog                                                          |    1 
lib/PFTools/Conf/Syntax.pm                                                |    1 
lib/PFTools/Parser.pm                                                     |    4 -
lib/PFTools/Structqueries.pm                                              |   20 +++------
t/10.parse.t                                                              |   14 +++---
t/13.conf.cfg1/config-export/SITE/cbv4-pfds/CONFIG/include-distrib-custom |   21 ++++++++++
t/13.conf.cfg1/config-export/SITE/cbv4-pfds/CONFIG/include-spawn-common   |    3 +

diffs (1613 lines):

diff -r 994c999ab677 -r 211fa4ce94e6 debian/changelog
--- a/debian/changelog	Fri Oct 15 14:10:14 2010 +0200
+++ b/debian/changelog	Wed Oct 20 17:07:06 2010 +0200
@@ -13,6 +13,8 @@
   * Parser_ini() now removes trailing comments.
   * Load_conf() now automagically resolves $file with Get_source(), using
     $hash_subst->{'HOSTNAME'} as the hostname.
+  * Two new accessors: get_default_pf_config() and get_current_pf_config().
+  * Parse_ini() has been renamed to parse_ini().
 
   [ Christophe Caillet ]
   * use remove_tree with keep_root option instead of remove_tree + make_path
@@ -31,7 +33,7 @@
   * using Module::Runtime for handling actions with a common API
   * using Module::Runtime for handling packages functions with a common API 
 
- -- Thomas Parmelan <tom at sitadelle.com>  Mon, 11 Oct 2010 07:36:25 +0200
+ -- Thomas Parmelan <tom at sitadelle.com>  Mon, 18 Oct 2010 16:18:42 +0200
 
 pf-tools (1.0-1) unstable; urgency=low
 
diff -r 994c999ab677 -r 211fa4ce94e6 lib/PFTools/Conf.pm
--- a/lib/PFTools/Conf.pm	Fri Oct 15 14:10:14 2010 +0200
+++ b/lib/PFTools/Conf.pm	Wed Oct 20 17:07:06 2010 +0200
@@ -29,6 +29,7 @@
 use Clone qw( clone );
 use English qw( -no_match_vars );    # Avoids regex performance penalty
 use Fcntl ':mode';
+use File::stat;
 use Net::Domain qw( hostname hostdomain );
 use Readonly;
 use Storable;
@@ -38,7 +39,7 @@
 use PFTools::Conf::Syntax;
 use PFTools::Logger;
 use PFTools::Net;
-use PFTools::Parser qw( Parser_ini );
+use PFTools::Parser qw( parse_ini );
 use PFTools::Structqueries;
 
 our @EXPORT = qw(
@@ -51,6 +52,8 @@
     Retrieve_GLOBAL
     Get_source
     Get_config_for_hostname_on_site
+    get_default_pf_config
+    get_current_pf_config
 );
 
 our @EXPORT_OK = qw();
@@ -73,11 +76,11 @@
 This hash describes the default configuration. Each key can be overrided from
 the configuration file (the default configuration file is /etc/pf-tools.conf).
 
-FIXME: documentation!
+FIXME documentation!
 
 =cut
 
-Readonly our $DEFAULT_PF_CONFIG => {
+Readonly my $DEFAULT_PF_CONFIG => {
     'path' => {
         'status_dir'     => '/var/lib/pftools',
         'distrib_dir'    => '/distrib',
@@ -132,7 +135,7 @@
         'hostname'       => $HOST_CONFIG_REGEX,
         'hosttype'       => $HOSTTYPE_CONFIG_REGEX,
         'deploy_hosts'   => $DEPLOY_CONFIG_REGEX,
-        'network_fstype' => qr{(nfs|cifs)},
+        'network_fstype' => qr{ (?:nfs|cifs) }xms,
     },
     'location' => {
         'site' => q{},
@@ -140,7 +143,27 @@
     },
 };
 
-our $PF_CONFIG = {};
+my $PF_CONFIG = {};
+
+=head2 get_default_pf_config()
+
+Returns the default (and read-only) pf_config.
+
+=cut
+
+sub get_default_pf_config {
+    return $DEFAULT_PF_CONFIG;
+}
+
+=head2 get_current_pf_config()
+
+Returns the current (and writable) pf_config.
+
+=cut
+
+sub get_current_pf_config {
+    return $PF_CONFIG;
+}
 
 =head2 Subst_vars( $text, $variables_ref )
 
@@ -183,21 +206,16 @@
         croak qq{ERROR: $config_file: no such file};
     }
 
-    # FIXME use File::stat ?
-    # FIXME stat or lstat ?
-    my ( $dev, $ino, $mode, $nlink, $uid, $gid, @lstat_vars )
-        = lstat($config_file);
+    my $st = stat $config_file;
 
-    # FIXME: also check that $uid == 0 && $gid == 0 ?
-    unless ( S_IMODE($mode) == 0600 && S_ISREG($mode) ) {
+    # FIXME also check that $uid == 0 && $gid == 0 ?
+    unless ( S_IMODE($st->mode) == 0600 and S_ISREG($st->mode) ) {
         croak
             qq{ERROR: weak rights for $config_file (check owner/group/mode)};
     }
 
-    my $conf_parsed = Parser_ini($config_file);
+    my $conf_parsed = parse_ini($config_file);
 
-    # FIXME use Hash::Merge::Simple instead ?
-    # (it would allow unknown sections/keys, is that a problem ?)
     foreach my $section ( keys %{$DEFAULT_PF_CONFIG} ) {
         next if !defined $conf_parsed->{$section};
         foreach my $key ( keys %{ $DEFAULT_PF_CONFIG->{$section} } ) {
@@ -214,7 +232,7 @@
 Initialize a hash structure with all the substitution variables needed
 to handle $hostname (default: the local host).
 
-FIXME: The variables are NOT documented.
+FIXME document the variables
 
 =cut
 
@@ -236,26 +254,27 @@
 
     my $host_regex = $pf_config->{'regex'}->{'hostname'}
         || $HOST_CONFIG_REGEX;
-    unless ( $hostname =~ m/$host_regex/ ) {
+    unless ( $hostname =~ m{ $host_regex }xms ) {
         croak qq{ERROR: Invalid hostname $hostname};
     }
 
     chomp( my $os_release = qx{ /bin/uname -r } );
 
-    my $hostnum = sprintf '%d', $+{HOSTDIGITS} || 0;
+    my $hostnum = sprintf '%d', $LAST_PAREN_MATCH{HOSTDIGITS} || 0;
 
     my $ref_subst = {
         HOSTNAME      => $hostname,
         DOMAINNAME    => $domainname,
-        HOSTTYPE      => $hosttype || $+{HOSTTYPE},
-        HOSTDIGITS    => $+{HOSTDIGITS},
+        HOSTTYPE      => $hosttype || $LAST_PAREN_MATCH{HOSTTYPE},
+        HOSTDIGITS    => $LAST_PAREN_MATCH{HOSTDIGITS},
         HOSTNUM       => $hostnum,
-        HOSTNODEINDEX => $+{HOSTNODEINDEX} || q{},
-        HOSTCLUSTER   => $+{HOSTDIGITS} . $+{HOSTNODEINDEX},
-        POPNAME       => $+{POPNAME} || q{},
-        OS_RELEASE    => $os_release,
-        HOSTMINUTE    => $hostnum % 60,
-        HOSTHOUR      => $hostnum % 24,
+        HOSTNODEINDEX => $LAST_PAREN_MATCH{HOSTNODEINDEX} || q{},
+        HOSTCLUSTER   => $LAST_PAREN_MATCH{HOSTDIGITS}
+            . $LAST_PAREN_MATCH{HOSTNODEINDEX},
+        POPNAME => $LAST_PAREN_MATCH{POPNAME} || q{},
+        OS_RELEASE => $os_release,
+        HOSTMINUTE => $hostnum % 60,
+        HOSTHOUR   => $hostnum % 24,
     };
 
     return $ref_subst;
@@ -269,7 +288,7 @@
 Optional parameters $hostname, $hash_subst and $pf_config will be computed if
 not specified.
 
-FIXME: The macros are NOT documented.
+FIXME document the macros
 
 The variables are defined by $hash_subst (defaults to the result of
 Init_SUBST()). See also Subst_vars().
@@ -294,7 +313,8 @@
     }
 
     my $vcs_work_dir = $pf_config->{'path'}->{'checkout_dir'}
-        or croak q{ERROR: Undefined configuration parameter: path.checkout_dir};
+        or croak
+        q{ERROR: Undefined configuration parameter: path.checkout_dir};
     my $module = $pf_config->{'vcs'}->{'module'}
         or croak q{ERROR: Undefined configuration parameter: vcs.module};
 
@@ -330,7 +350,7 @@
 Returns undef if empty or undefined arguments.
 Croaks on other errors.
 
-FIXME: add more documentation here
+FIXME add more documentation here
 
 =cut
 
@@ -353,47 +373,49 @@
     }
 
     if ( $context !~ m{ \A $ALLOWED_PARSING_CONTEXT \z }xms ) {
-        croak qq{ERROR: Invalid context $context for file $file: doesn't match $ALLOWED_PARSING_CONTEXT};
+        croak
+            qq{ERROR: Invalid context $context for file $file: doesn't match $ALLOWED_PARSING_CONTEXT};
     }
 
     # Automagically resolve $file with Get_source()
-    my $hostname = $hash_subst->{'HOSTNAME'}; # FIXME or croak ?
+    # NOTE: $hostname will default to localhost in Init_SUBST(), so there's no
+    # need to err if it is not defined here
+    my $hostname = $hash_subst->{'HOSTNAME'};
     my $real_file = Get_source( $file, $hostname, $hash_subst, $pf_config );
 
-    # this will croak() on error
-    my $parsed = Parser_ini($real_file);
+    # This will properly croak() on error
+    my $parsed = parse_ini($real_file);
 
-    my $select = $context eq 'config' ? 'action' : 'type'; # compute it only once
+    my $action_or_type
+        = $context eq 'config' ? 'action' : 'type';    # compute it only once
 
-    # FIXME: some code factorization seems possible, but
+    # FIXME some code factorization seems possible, but
     # proper tests are needed before changing things here.
 
     if ( $context eq 'host' or $context eq 'model' ) {
         if ( defined $parsed->{'hostgroup'}->{'model'} ) {
-            $parsed->{'hostgroup'}->{'__model'} = Load_conf(
-                Get_source( $parsed->{'hostgroup'}->{'model'}, q{}, $hash_subst, $pf_config), # FIXME auto
-                $hash_subst,
-                'model',
-                $pf_config
-            );
+            $parsed->{'hostgroup'}->{'__model'}
+                = Load_conf(
+                $parsed->{'hostgroup'}->{'model'},
+                $hash_subst, 'model', $pf_config
+                );
         }
     }
     else {
         foreach my $section ( keys %{$parsed} ) {
-            next if $section =~ m{ \A __ }xms; # skip "internal" sections
+            next if $section =~ m{ \A __ }xms;    # skip "internal" sections
 
-            if ( !defined $parsed->{$section}->{$select} ) {
-                croak qq{Key $select must be defined in section $section from file $file};
+            my $section_type = $parsed->{$section}->{$action_or_type};
+            if ( !defined $section_type ) {
+                croak
+                    qq{Key $action_or_type must be defined in section $section from file $file};
             }
 
-            my $section_type = $parsed->{$section}->{$select};
             if ( $section_type eq 'include' ) {
-
-                # We need to dive into deep ...
                 $parsed->{$section}->{'__content'}
                     = Load_conf(
-                        Get_source( $section, q{}, $hash_subst, $pf_config ), # FIXME auto
-                        $hash_subst, $context, $pf_config
+                    $section, $hash_subst, $context,
+                    $pf_config
                     );
             }
         }
@@ -404,54 +426,75 @@
 
     # Basic checks
     foreach my $section ( keys %{$parsed} ) {
-        next if $section =~ m{ \A __ }xms; # skip "internal" sections
+        next if $section =~ m{ \A __ }xms;    # skip "internal" sections
 
         my $section_type;
         if ( $context eq 'host' or $context eq 'model' ) {
             unless ( $section =~ m{ \A ([^:]+) (?: :: (.+) )? \z }xms ) {
-                croak qq{ERROR: Unable to compute section type for section $section};
+                croak
+                    qq{ERROR: Unable to compute section type for section $section};
             }
             $section_type = $1;
+
             # $iface_name = $2;
         }
         else {
-            if ( !defined $parsed->{$section}->{$select} ) {
-                croak qq{Key $select must be defined in section $section from file $file};
+            $section_type = $parsed->{$section}->{$action_or_type};
+            if ( !defined $section_type ) {
+                croak
+                    qq{Key $action_or_type must be defined in section $section from file $file};
             }
-            $section_type = $parsed->{$section}->{$select};
         }
 
-        Chk_section_struct( $section, $section_type, $parsed->{$section},
-            $context );
+        Chk_section_struct(
+            $section, $section_type, $parsed->{$section},
+            $context
+        );
     }
 
     return $parsed;
 }
+
+=head2 Init_GLOBAL_NETCONFIG( $start_file, $hash_subst, $pf_config )
+
+Loads $start_file and returns the corresponding structure.
+
+=cut
 
 sub Init_GLOBAL_NETCONFIG {
     my ( $start_file, $hash_subst, $pf_config ) = @_;
 
     return unless $start_file and $hash_subst;
 
-    if ( !defined $pf_config ) {
-        $pf_config = $PF_CONFIG;
+    $pf_config ||= $PF_CONFIG;    # default
+
+    if ( ref $start_file ) {
+        croak q{ERROR: Invalid non-scalar $start_file};
+    }
+    if ( ref $hash_subst ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref $hash_subst};
+    }
+    if ( ref $pf_config ne 'HASH' ) {
+        croak q{ERROR: Invalid non-hashref $pf_config};
     }
 
     my $GLOBAL = { 'SITE' => { 'BY_NAME' => {}, } };
     foreach my $ip_type ( 'ipv4', 'ipv6' ) {
-        next if !$pf_config->{'features'}->{$ip_type};
-        my $zone_key = ( $ip_type eq 'ipv6' ) ? 'ZONE6' : 'ZONE';
-        my $dhcp_key = ( $ip_type eq 'ipv6' ) ? 'DHCP6' : 'DHCP';
+        next unless $pf_config->{'features'}->{$ip_type};
+
+        my $zone_key = $ip_type eq 'ipv6' ? 'ZONE6' : 'ZONE';
+        my $dhcp_key = $ip_type eq 'ipv6' ? 'DHCP6' : 'DHCP';
         $GLOBAL->{$zone_key} = {
             'BY_NAME' => {},
-            'BY_SITE' => {}
+            'BY_SITE' => {},
         };
         $GLOBAL->{$dhcp_key} = { 'BY_SITE' => {} };
     }
 
     my $net_parsed
         = Load_conf( $start_file, $hash_subst, 'network', $pf_config );
-    my @sortnetkeys = sort { __Sort_net_section( $net_parsed, $a, $b ) }
+    my @sortnetkeys
+        = sort { __Sort_net_section( $net_parsed, $a, $b ) }
         @{ $net_parsed->{'__sections_order'} };
     foreach my $section (@sortnetkeys) {
         if ( $net_parsed->{$section}->{'type'} eq 'zone' ) {
@@ -559,43 +602,68 @@
     return $ret;
 }
 
+=head2 Get_config_for_hostname_on_site( $hostname, $site, $hash_subst, $global_config, $pf_config )
+
+Returns the parsed configuration for $hostname on $site.
+
+=cut
+
 sub Get_config_for_hostname_on_site {
     my ( $hostname, $site, $hash_subst, $global_config, $pf_config ) = @_;
 
+    if (ref $hostname) {
+        croak q{ERROR: Invalid non-scalar $hostname};
+    }
+    if (not $hostname) {
+        croak q{ERROR: Invalid empty or undefined $hostname};
+    }
+
+    if (ref $site) {
+        croak q{ERROR: Invalid non-scalar $site};
+    }
+    if (not $site) {
+        croak q{ERROR: Invalid empty or undefined $site};
+    }
+
+    if (ref $hash_subst ne 'HASH') {
+        croak q{ERROR: Invalid non-hashref $hash_subst};
+    }
+
+    if (ref $global_config ne 'HASH') {
+        croak q{ERROR: Invalid non-hashref $global_config};
+    }
+
+    if (ref $pf_config ne 'HASH') {
+        croak q{ERROR: Invalid non-hashref $pf_config};
+    }
+
     # Common configuration file e.g. update-common
     my $global_host_conf = Load_conf(
-        # FIXME test if the auto-Get_source() with $hash_subst->{'HOSTNAME}' is OK here
-        Get_source(
-            'COMMON:/' . $pf_config->{'path'}->{'common_config'},
-            $hostname, $hash_subst, $pf_config
-        ),
+        'COMMON:/' . $pf_config->{'path'}->{'common_config'},
         $hash_subst,
         'config',
         $pf_config
     );
     my $hosttype
         = Get_hosttype_from_hostname( $hostname, $global_config, $site );
-    if ( !defined $hosttype ) {
-        Abort(
-            $CODE->{'UNDEF_KEY'},
-            "Unable to get hosttype from hostname $hostname for getting hosttype configuration file"
-        );
+    unless ($hosttype) {
+        croak qq{Unable to get hosttype from hostname $hostname};
     }
 
     # Hosttype configuration file e.g. update-<hosttype>
-    my $hosttype_conf_file
-        = __Get_config_path( $hosttype, $pf_config, $site );
+    my $hosttype_file = __Get_config_path( $hosttype, $pf_config, $site );
 
     # Hostname configuration file e.g. update-<hostname>
-    my $hostname_conf_file
-        = __Get_config_path( $hostname, $pf_config, $site );
+    my $hostname_file = __Get_config_path( $hostname, $pf_config, $site );
 
-    foreach my $file ( $hosttype_conf_file, $hostname_conf_file ) {
-        next if !defined $file;
+    foreach my $file ( $hosttype_file, $hostname_file ) {
+        next unless $file;
+
         my $config = Load_conf( $file, $hash_subst, 'config', $pf_config );
         foreach my $section ( @{ $config->{'__sections_order'} } ) {
-            push @{ $global_host_conf->{'__sections_order'} }, $section
-                if !defined $global_host_conf->{$section};
+            unless ( $global_host_conf->{$section} ) {
+                push @{ $global_host_conf->{'__sections_order'} }, $section;
+            }
             $global_host_conf->{$section} = $config->{$section};
         }
     }
@@ -612,7 +680,7 @@
 =head2 __Get_config_path( $host_value, $pf_config, $site ) -- NOT EXPORTED
 
 Given a host value (host name or host type) and a site, returns the path to the
-site-specific configuration file this host value if it exists, or to the
+site-specific configuration file for this host value if it exists, or to the
 non-site-specific configuration file for this host value if it exists.
 
 Returns an undefined value on invalid arguments or if no suitable find was
@@ -645,6 +713,7 @@
     return;
 }
 
+# FIXME doc and tests
 sub __Merge_host_config {
     my ( $hash_to_merge, $hash_subst ) = @_;
 
@@ -679,8 +748,7 @@
     return $merge;
 }
 
-# sub __Merge_other_context
-
+# FIXME doc and tests
 sub __Merge_conf_includes {
     my ( $hash_to_merge, $hash_subst, $context ) = @_;
 
diff -r 994c999ab677 -r 211fa4ce94e6 lib/PFTools/Conf/Host.pm
--- a/lib/PFTools/Conf/Host.pm	Fri Oct 15 14:10:14 2010 +0200
+++ b/lib/PFTools/Conf/Host.pm	Wed Oct 20 17:07:06 2010 +0200
@@ -28,7 +28,6 @@
 use POSIX qw(ceil floor);
 
 use PFTools::Conf::Syntax qw( $DEF_SECTIONS );
-use PFTools::Logger;
 use PFTools::Net;
 use PFTools::Structqueries;
 
@@ -620,23 +619,24 @@
     # Checking path for PXE elements kernel, initrd ...
     my $boot_def = $DEF_SECTIONS->{'host'}->{'boot'};
     foreach my $key ( keys %{$boot_def} ) {
-        next if( $key =~ m{\A MANDATORY}xms );
+        next if $key =~ m{ \A MANDATORY }xms;
         my $value = $host2add->{'boot'}->{"$key\.$host_number"}
             || $host2add->{'boot'}->{$key};
         unless ( $value ) {
-            if ( $key =~ m{\A (console|cmdline) \Z}xms ) {
+            if ( $key =~ m{ \A (console|cmdline) \z }xms ) {
                 $value = ( $key eq 'console' )
                     ? $site_part->{$key}
-                    : "";
+                    : q{};
             }
         }
-        next unless( $value );
-        if (
-            $key !~ m{\A (console|cdline) \Z}xms
-            && ! -e $pf_config->{'path'}->{'tftp_dir'} . $value
-        ) {
-            Warn( $CODE->{'OPEN'},
-                "$value in $pf_config->{'path'}->{'tftp_dir'} doesn't exist" );
+        next unless $value;
+
+        # FIXME this should be done on the deploy-host only
+        if ( $key !~ m{ \A (console|cmdline) \z }xms
+            && !-e $pf_config->{'path'}->{'tftp_dir'} . $value )
+        {
+            carp
+                qq{WARN: $value in $pf_config->{'path'}->{'tftp_dir'} doesn't exist};
         }
         $result->{$key} = $value;
     }
diff -r 994c999ab677 -r 211fa4ce94e6 lib/PFTools/Conf/Syntax.pm
--- a/lib/PFTools/Conf/Syntax.pm	Fri Oct 15 14:10:14 2010 +0200
+++ b/lib/PFTools/Conf/Syntax.pm	Wed Oct 20 17:07:06 2010 +0200
@@ -1,4 +1,5 @@
 package PFTools::Conf::Syntax;
+
 #
 #  Copyright (C) 2010 Christophe Caillet <quadchris at free.fr>
 #
@@ -297,7 +298,7 @@
             'after_change'  => 'undefined',
         },
         'filter-model' => {
-            'MANDATORY_KEYS' => [ 'filter' ],
+            'MANDATORY_KEYS' => ['filter'],
             'filter'         => 'undefined',
         },
         'actiongroup' => {
@@ -331,9 +332,10 @@
         croak qq{ERROR: Invalid section type $section_type};
     }
 
-    my ( $iface_type, $section_tmp);
+    my ( $iface_type, $section_tmp );
     if ( $context eq 'host' or $context eq 'model' ) {
-        unless ($section_name
+        unless (
+            $section_name
             =~ m{
                 \A
                 \Q$section_type\E
@@ -354,6 +356,7 @@
             croak qq{ERROR: Invalid section name $section_name};
         }
         $iface_type = $+{iftype};
+
         # Clean key names by removing .default or .%HOSTNUM% suffix
         foreach my $key ( keys %{$section_hash} ) {
             my $new = $key;
@@ -375,31 +378,32 @@
         }
         last if $section_type eq 'hostgroup' and $context eq 'model';
         if ( !defined $section_tmp->{$key} ) {
-            croak qq{Mandatory key $key must be defined in section $section_name in context $context};
+            croak
+                qq{Mandatory key $key must be defined in section $section_name in context $context};
         }
     }
 
     # Check all keys defined
     foreach my $key ( keys %{$definition} ) {
         next
-            if     $key eq 'MANDATORY_KEYS'
+            if $key eq 'MANDATORY_KEYS'
                 or $key =~ m{\A __}xms
-                or $definition->{$key} eq 'undefined' # FIXME
-                or !defined $section_tmp->{$key};
+                or $definition->{$key} eq 'undefined'    # FIXME
+                or not defined $section_tmp->{$key};
 
         my ( $tab_values, $key_name );
         if ( $int_context eq 'host' ) {
             $tab_values
                 = ( $key !~ /^@/ )
                 ? [ $section_tmp->{$key}->{'VALUE'} ]
-                :   $section_tmp->{$key}->{'VALUE'};
+                : $section_tmp->{$key}->{'VALUE'};
             $key_name = $section_tmp->{$key}->{'ORIG_NAME'};
         }
         else {
             $tab_values
                 = ( $key !~ /^@/ )
                 ? [ $section_tmp->{$key} ]
-                :   $section_tmp->{$key};
+                : $section_tmp->{$key};
             $key_name = $key;
         }
 
@@ -410,7 +414,8 @@
             $value =~ s{\s* \z}{}xms;
 
             if ( $value !~ m{ \A $definition->{$key} \z }xms ) {
-                croak qq{Value '$value' for key $key_name in section $section_name doesn't match $definition->{$key}};
+                croak
+                    qq{Value '$value' for key $key_name in section $section_name doesn't match $definition->{$key}};
             }
         }
     }
diff -r 994c999ab677 -r 211fa4ce94e6 lib/PFTools/Parser.pm
--- a/lib/PFTools/Parser.pm	Fri Oct 15 14:10:14 2010 +0200
+++ b/lib/PFTools/Parser.pm	Wed Oct 20 17:07:06 2010 +0200
@@ -28,22 +28,26 @@
 
 use PFTools::Logger;
 
-our @EXPORT_OK = qw( Parser_ini );
+our @EXPORT_OK = qw( parse_ini );
 
 my $trailing_comment_regex = qr{ \s+ [#] .* \z }xms;
 
-sub Parser_ini {
+sub parse_ini {
     my ($file) = @_;
 
     return unless $file;
 
     # Config::IniFiles never croaks (it only carps), so eval is useless :/
-    my $parse = Config::IniFiles->new( -file => $file, -allowcontinue => 1 );
+    my $parse = Config::IniFiles->new(
+        -file          => $file,
+        -allowcontinue => 1,
+        -allowempty    => 1,
+    );
 
     # @Config::IniFiles::errors is only used for parse errors
     if (@Config::IniFiles::errors) {
         croak "ERROR: Unable to parse $file: ",
-            join( "\n", @Config::IniFiles::errors );
+            join "\n", @Config::IniFiles::errors;
     }
     if ( !$parse ) {
         croak "ERROR: Unable to load $file";
@@ -62,18 +66,19 @@
 
             # ..and .all keys starting with '@' must be arrays
             elsif ( $key_name =~ m{ \A @ }xms and ref $value ne 'ARRAY' ) {
-                $section->{$key_name} = [ $value ];
+                $section->{$key_name} = [$value];
             }
 
             # Second, remove trailing spaces in arrays...
             if ( ref $section->{$key_name} eq 'ARRAY' ) {
-                map { $_ =~ s{$trailing_comment_regex}{}; }
-                    @{ $section->{$key_name} };
+                foreach my $value ( @{ $section->{$key_name} } ) {
+                    $value =~ s{$trailing_comment_regex}{}xms;
+                }
             }
 
             # ...and in scalars
             else {
-                $section->{$key_name} =~ s{$trailing_comment_regex}{};
+                $section->{$key_name} =~ s{$trailing_comment_regex}{}xms;
             }
 
         }
diff -r 994c999ab677 -r 211fa4ce94e6 lib/PFTools/Structqueries.pm
--- a/lib/PFTools/Structqueries.pm	Fri Oct 15 14:10:14 2010 +0200
+++ b/lib/PFTools/Structqueries.pm	Wed Oct 20 17:07:06 2010 +0200
@@ -84,21 +84,34 @@
     return $global_config->{'SITE'}->{'BY_NAME'}->{$site}->{'zone'};
 }
 
-#########################################################################
-#
-# STR Get_hosttype_from_hostname ( STR, STR, STR, STR, HASHREF )
-#
-# This function returns the hostname for a given model, number and node
-# Inputs :
-#  - $hostname		: string containing the model definition for building hostname
-#  - $global_config	: hashref containing the parsed global configuration
-#  - $site			: define here the site where hostname is defined
-#
-# Output :
-#  Returns a string containing the hosttype or undef if hostname doesn't exist
-#
+=head2
+
+Get_hosttype_from_hostname ( $hostname, $global_config, $site )
+
+Returns the hosttype, or nothing.
+
+=cut
+
 sub Get_hosttype_from_hostname {
     my ( $hostname, $global_config, $site ) = @_;
+
+    if (not $hostname) {
+        croak q{ERROR: Invalid empty or undefined $hostname};
+    }
+    if (ref $hostname) {
+        croak q{ERROR: Invalid non-scalar $hostname};
+    }
+
+    if (not $global_config) {
+        croak q{ERROR: Invalid empty or undefined $global_config};
+    }
+    if (ref $global_config ne 'HASH') {
+        croak q{ERROR: Invalid non-hashref $global_config};
+    }
+
+    if ($site and ref $site) {
+        croak q{ERROR: Invalid non-scalar $site};
+    }
 
     my $site_list = ( $site )
         ? [ $site ]
@@ -109,12 +122,14 @@
             = $global_config->{'SITE'}->{'BY_NAME'}->{$site}->{'HOST'}
             ->{'BY_NAME'};
         foreach my $hostclass ( keys %{$host_part} ) {
-            return $hostclass if ( $hostclass eq $hostname );
+            return $hostclass if $hostclass eq $hostname;
+
             foreach my $host ( keys %{ $host_part->{$hostclass} } ) {
-                return $hostclass if ( $host eq $hostname );
+                return $hostclass if $host eq $hostname;
             }
         }
     }
+
     return;
 }
 
diff -r 994c999ab677 -r 211fa4ce94e6 t/10.parse.t
--- a/t/10.parse.t	Fri Oct 15 14:10:14 2010 +0200
+++ b/t/10.parse.t	Wed Oct 20 17:07:06 2010 +0200
@@ -6,34 +6,37 @@
 use Test::Exception;
 use Test::More qw( no_plan );
 
-use PFTools::Parser qw( Parser_ini );
+use PFTools::Parser qw( parse_ini );
 
-can_ok( 'PFTools::Parser', qw( Parser_ini ) );
+can_ok( 'PFTools::Parser', qw( parse_ini ) );
 
-note('Testing PFTools::Parser::Parser_ini');
+note('Testing PFTools::Parser::parse_ini');
 
-ok !defined( Parser_ini() )
+ok !defined( parse_ini() )
     => 'Returns undef if no file specified';
 
-throws_ok { Parser_ini('/dev/null') }
-    qr{\A ERROR: }xms
-    => 'Dies on empty file';
+my $expected_cfg = {
+  '__sections_order' => [],
+};
 
-throws_ok { Parser_ini('/nonexistent.ini') }
+lives_and { is_deeply parse_ini('/dev/null'), $expected_cfg }
+    'Lives on empty file and returns the correct hash';
+
+throws_ok { parse_ini('/nonexistent.ini') }
     qr{\A ERROR: }xms
     => 'Dies on nonexistent file';
 
 my $cfg;
-throws_ok { Parser_ini('t/10.parse.cfg1') }
+throws_ok { parse_ini('t/10.parse.cfg1') }
     qr{\A ERROR: }xms
     => 'Dies on non-ini file';
 
 my $file = 't/10.parse.cfg2';
-$cfg = Parser_ini($file);
+$cfg = parse_ini($file);
 ok defined($cfg)
     => qq{Returns something on real ini file $file};
 
-my $expected_cfg = {
+$expected_cfg = {
   '__sections_order' => [
     'section1',
     'section2',
@@ -97,7 +100,7 @@
     },
 };
 $file = 't/13.conf.cfg1/etc/pf-tools.1.conf';
-$cfg = Parser_ini($file);
+$cfg = parse_ini($file);
 is_deeply $cfg, $expected_cfg
     => qq{Returns the correct hash on real ini file $file}
     or note explain $cfg;
diff -r 994c999ab677 -r 211fa4ce94e6 t/13.conf.cfg1/config-export/SITE/cbv4-pfds/CONFIG/include-distrib-custom
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/13.conf.cfg1/config-export/SITE/cbv4-pfds/CONFIG/include-distrib-custom	Wed Oct 20 17:07:06 2010 +0200
@@ -0,0 +1,42 @@
+#
+# Custom distrib tools and config file
+
+
+[apt-utils]
+	action		= apt-get
+
+[bzip2]
+	action		= apt-get
+
+#
+# Config files for generating custom repositories
+[/etc/apt/apt-ftp-debian.conf]
+	depends		= apt-utils
+        action		= addfile
+        source		= CONFIG:spawn%SECTIONNAME%
+        owner		= root
+        group		= root
+        mode		= 0640
+	on_noaction	= true
+        after_change	= [ -x /usr/local/sbin/update-apt-ftparchive ] && /usr/local/sbin/update-apt-ftparchive debian
+
+[/etc/apt/apt-ftp-ubuntu.conf]
+	depends		= apt-utils
+        action		= addfile
+        source		= CONFIG:spawn%SECTIONNAME%
+        owner		= root
+        group		= root
+        mode		= 0640
+	on_noaction	= true
+        after_change	= [ -x /usr/local/sbin/update-apt-ftparchive ] && /usr/local/sbin/update-apt-ftparchive ubuntu
+
+[/usr/local/sbin/update-apt-ftparchive]
+	depends		= /etc/apt/apt-ftp-debian.conf /etc/apt/apt-ftp-ubuntu.conf bzip2
+	action		= addfile
+	source		= CONFIG:spawn%SECTIONNAME%
+	owner		= root
+	group		= root
+	mode		= 0750
+	on_noaction	= /usr/local/sbin/update-apt-ftparchive
+	after_change	= /usr/local/sbin/update-apt-ftparchive
+
diff -r 994c999ab677 -r 211fa4ce94e6 t/13.conf.cfg1/config-export/SITE/cbv4-pfds/CONFIG/include-spawn-common
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/13.conf.cfg1/config-export/SITE/cbv4-pfds/CONFIG/include-spawn-common	Wed Oct 20 17:07:06 2010 +0200
@@ -0,0 +1,6 @@
+#
+# include-spawn-common
+#
+
+# (many things are here in real life)
+
diff -r 994c999ab677 -r 211fa4ce94e6 t/13.conf.t
--- a/t/13.conf.t	Fri Oct 15 14:10:14 2010 +0200
+++ b/t/13.conf.t	Wed Oct 20 17:07:06 2010 +0200
@@ -14,6 +14,7 @@
 use Test::More qw( no_plan );
 
 use PFTools::Conf;
+use PFTools::Structqueries;
 
 ########################################################################
 note('Testing PFTools::Conf::__Sort_net_section');
@@ -24,7 +25,6 @@
 is PFTools::Conf::__Sort_net_section( {}, 'foo', 'bar' ), 0
     => 'Returns 0 if invalid parsed_net arg';
 
-# FIXME add a more complex/complete test
 my $parsed_net = {
     foo => {
         type => 'server',
@@ -49,13 +49,18 @@
 note('Testing PFTools::Conf::Init_PF_CONFIG');
 can_ok( 'PFTools::Conf', qw( Init_PF_CONFIG ) );
 
-my $default_pf_config = $PFTools::Conf::DEFAULT_PF_CONFIG;
+my $default_pf_config = get_default_pf_config();
 ok defined $default_pf_config
-    => '$PF_CONFIG defined';
+    => '$DEFAULT_PF_CONFIG defined';
 ok ref $default_pf_config eq 'HASH'
-    => '$PF_CONFIG is a hashref';
+    => '$DEFAULT_PF_CONFIG is a hashref';
 ok keys %{$default_pf_config}
-    => '$PF_CONFIG is a non-empty hashref';
+    => '$DEFAULT_PF_CONFIG is a non-empty hashref';
+
+my $current_pf_config = get_current_pf_config();
+is_deeply $current_pf_config, {}
+    => 'Empty $PF_CONFIG'
+    or note explain $current_pf_config;
 
 my $config = Init_PF_CONFIG();
 is_deeply $config, $default_pf_config
@@ -101,6 +106,11 @@
 is_deeply $parsed_configuration, $expected_configuration
     => 'Correctly merges with the default configuration'
     or note explain $parsed_configuration;
+
+$current_pf_config = get_current_pf_config();
+is_deeply $current_pf_config, $expected_configuration
+    => 'Correctly sets $PF_CONFIG'
+    or note explain $current_pf_config;
 
 unlink $test_config_file
     or die "unlink $test_config_file: $OS_ERROR";
@@ -689,8 +699,702 @@
     or note explain $parsed_configuration;
 
 
-# FIXME contexts: config
-diag( qq{FIXME: add other files in $test_config_dir to test Load_conf() with real files and other contexts} );
+diag( qq{FIXME: add other files in $test_config_dir to test Load_conf() with real files and 'config' context} );
+
+
+########################################################################
+note('Testing PFTools::Conf::Init_GLOBAL_NETCONFIG');
+can_ok( 'PFTools::Conf', qw( Init_GLOBAL_NETCONFIG ) );
+
+ok !defined Init_GLOBAL_NETCONFIG()
+    => 'Returns undef if no args';
+
+ok !defined Init_GLOBAL_NETCONFIG( 'start_file' )
+    => 'Returns undef if only one arg';
+
+throws_ok { Init_GLOBAL_NETCONFIG( {}, {} ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] start_file }xms
+    => q{Dies if non-scalar $start_file};
+
+throws_ok { Init_GLOBAL_NETCONFIG( 'start_file', 'hash_subst' ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] hash_subst }xms
+    => q{Dies if non-hashref $hash_subst};
+
+throws_ok { Init_GLOBAL_NETCONFIG( 'start_file', {}, 'pf_config' ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] pf_config }xms
+    => q{Dies if non-hashref $pf_config};
+
+$parsed_configuration = Init_GLOBAL_NETCONFIG( q{COMMON:private-network}, $test_hash_subst, $test_pf_config );
+$expected_configuration = {
+    'DHCP' => {
+        'BY_SITE' => {
+            'cbv4-pfds' => {
+                'vlan-systeme' => {
+                    'cbv4-spawn' => {
+                        'cbv4-spawn01' => [
+                            'hardware ethernet 00:1e:c9:ff:42:0a;',
+                            'fixed-address 10.1.167.1;',
+                            'filename pxelinux.0;',
+                            'option domain-name-servers nsprivate.private,spawn.private;'
+                        ],
+                        'cbv4-spawn00' => [
+                            'hardware ethernet 00:1e:c9:ff:08:e3;',
+                            'fixed-address 10.1.167.0;',
+                            'filename pxelinux.0;',
+                            'option domain-name-servers nsprivate.private,spawn.private;'
+                            ]
+                    },
+                    'netmask' => '255.255.0.0',
+                    'subnet'  => '10.1.0.0'
+                    }
+            },
+            'cbv4' => {
+                'vlan-systeme' => {
+                    'netmask' => '255.255.0.0',
+                    'subnet'  => '10.1.0.0'
+                    }
+                }
+            }
+    },
+    'SITE' => {
+        'EDGE' => [
+            'cbv4'
+        ],
+        'ROOT'    => 'cbv4-pfds',
+        'BY_NAME' => {
+            'cbv4-pfds' => {
+                'HOST' => {
+                    'BY_NAME' => {
+                        'vip-spawn' => {
+                            'nscache'    => 'vip-spawn',
+                            'vip-deploy' => 'vip-spawn',
+                            'vip-spawn'  => {
+                                'interfaces' => {
+                                    'eth1' => {
+                                        'ipv4' => '10.1.1.254/16',
+                                        'vlan' => 'vlan-systeme'
+                                    },
+                                    'eth0' => {
+                                        'ipv4' => '192.168.1.99/24',
+                                        'vlan' => 'vlan-pfds-ext'
+                                        }
+                                },
+                                'deployment' => {
+                                    'hosttype'       => 'vip-spawn',
+                                    'order'          => '2',
+                                    'hostname_model' => 'vip-spawn'
+                                    }
+                            },
+                            'mf'        => 'vip-spawn',
+                            'mirrors'   => 'vip-spawn',
+                            'nsprivate' => 'vip-spawn',
+                            'cvs'       => 'vip-spawn'
+                        },
+                        'cbv4-spawn' => {
+                            'ntp01'        => 'cbv4-spawn01',
+                            'ntp'          => 'cbv4-spawn',
+                            'cbv4-spawn01' => {
+                                'dns' => {
+                                    'resolver' =>
+                                        'nsprivate.private,spawn.private'
+                                },
+                                'boot' => {
+                                    'kernel' =>
+                                        'vmlinuz-2.6.26.5-universal-grm2.1.12',
+                                    'pxefilename' => 'pxelinux.0',
+                                    'console'     => 'default'
+                                },
+                                'interfaces' => {
+                                    'eth4' => {
+                                        'ipv4' => '10.3.1.42/24',
+                                        'vlan' => 'vlan-admindsi'
+                                    },
+                                    'eth5' => {
+                                        'ipv4' => '10.3.2.42/24',
+                                        'vlan' => 'vlan-middledsi'
+                                    },
+                                    'bond0' => {
+                                        'ipv4'    => '192.168.1.98/24',
+                                        'options' => 'miimon=100',
+                                        'mode'    => 'active-backup',
+                                        'slaves'  => 'eth2 eth3',
+                                        '@route'  => [
+                                            'default via 192.168.1.254'
+                                        ],
+                                        'vlan' => 'vlan-pfds-ext'
+                                    },
+                                    'eth0' => {
+                                        'ipv4' => '10.1.167.1/16',
+                                        'mac'  => '00:1e:c9:ff:42:0a',
+                                        'vlan' => 'vlan-systeme'
+                                        }
+                                },
+                                'deployment' => {
+                                    'dhcpvlan'       => 'vlan-systeme',
+                                    'hosttype'       => 'cbv4-spawn',
+                                    'arch'           => 'amd64',
+                                    'mode'           => 'debian',
+                                    'order'          => '1',
+                                    'distrib'        => 'lenny',
+                                    'hostname_model' => 'cbv4-spawn%%'
+                                    }
+                            },
+                            'cbv4-spawn00' => {
+                                'dns' => {
+                                    'resolver' =>
+                                        'nsprivate.private,spawn.private'
+                                },
+                                'boot' => {
+                                    'kernel' =>
+                                        'vmlinuz-2.6.26.5-universal-grm2.1.12',
+                                    'pxefilename' => 'pxelinux.0',
+                                    'console'     => 'default'
+                                },
+                                'interfaces' => {
+                                    'eth4' => {
+                                        'ipv4' => '10.3.1.41/24',
+                                        'vlan' => 'vlan-admindsi'
+                                    },
+                                    'eth5' => {
+                                        'ipv4' => '10.3.2.41/24',
+                                        'vlan' => 'vlan-middledsi'
+                                    },
+                                    'bond0' => {
+                                        'ipv4'    => '192.168.1.97/24',
+                                        'options' => 'miimon=100',
+                                        'mode'    => 'active-backup',
+                                        'slaves'  => 'eth2 eth3',
+                                        '@route'  => [
+                                            'default via 192.168.1.254'
+                                        ],
+                                        'vlan' => 'vlan-pfds-ext'
+                                    },
+                                    'eth0' => {
+                                        'ipv4' => '10.1.167.0/16',
+                                        'mac'  => '00:1e:c9:ff:08:e3',
+                                        'vlan' => 'vlan-systeme'
+                                        }
+                                },
+                                'deployment' => {
+                                    'dhcpvlan'       => 'vlan-systeme',
+                                    'hosttype'       => 'cbv4-spawn',
+                                    'arch'           => 'amd64',
+                                    'mode'           => 'debian',
+                                    'order'          => '1',
+                                    'distrib'        => 'lenny',
+                                    'hostname_model' => 'cbv4-spawn%%'
+                                    }
+                            },
+                            'spawn00' => 'cbv4-spawn00',
+                            'spawn01' => 'cbv4-spawn01',
+                            'ntp00'   => 'cbv4-spawn00',
+                            'spawn'   => 'cbv4-spawn'
+                        },
+                        'cbv4-pfds-filer' => {
+                            'cbv4-pfds-filer01' => {
+                                'interfaces' => {
+                                    'eth0' => {
+                                        'ipv4' => '10.1.2.1/16',
+                                        'vlan' => 'vlan-systeme'
+                                        }
+                                },
+                                'deployment' => {
+                                    'hosttype'       => 'cbv4-pfds-filer',
+                                    'order'          => '2',
+                                    'hostname_model' => 'cbv4-pfds-filer%%'
+                                    }
+                            },
+                            'cbv4-pfds-filer00' => {
+                                'interfaces' => {
+                                    'eth0' => {
+                                        'ipv4' => '10.1.2.0/16',
+                                        'vlan' => 'vlan-systeme'
+                                        }
+                                },
+                                'deployment' => {
+                                    'hosttype'       => 'cbv4-pfds-filer',
+                                    'order'          => '2',
+                                    'hostname_model' => 'cbv4-pfds-filer%%'
+                                    }
+                                }
+                            }
+                    },
+                    '__hostclass_pxe' => [
+                        'cbv4-spawn'
+                    ],
+                    'BY_ADDR' => {
+                        '192.168.1.98/24' => 'cbv4-spawn01.vlan-pfds-ext',
+                        '10.3.1.41/24'    => 'cbv4-spawn00.vlan-admindsi',
+                        '10.3.2.41/24'    => 'cbv4-spawn00.vlan-middledsi',
+                        '10.1.2.1/16'     => 'cbv4-pfds-filer01.vlan-systeme',
+                        '10.1.2.0/16'     => 'cbv4-pfds-filer00.vlan-systeme',
+                        '10.3.1.42/24'    => 'cbv4-spawn01.vlan-admindsi',
+                        '192.168.1.99/24' => 'vip-spawn.vlan-pfds-ext',
+                        '10.1.167.0/16'   => 'cbv4-spawn00.vlan-systeme',
+                        '10.1.167.1/16'   => 'cbv4-spawn01.vlan-systeme',
+                        '10.3.2.42/24'    => 'cbv4-spawn01.vlan-middledsi',
+                        '192.168.1.97/24' => 'cbv4-spawn00.vlan-pfds-ext',
+                        '10.1.1.254/16'   => 'vip-spawn.vlan-systeme'
+                    },
+                    'BY_MAC' => {
+                        '00:1e:c9:ff:42:0a' =>
+                            'eth0.cbv4-spawn01.vlan-systeme',
+                        '00:1e:c9:ff:08:e3' =>
+                            'eth0.cbv4-spawn00.vlan-systeme'
+                        }
+                },
+                'location' => 'Courbevoie',
+                'zone'     => 'private',
+                'console'  => 'default',
+                'SERVICE'  => {
+                    'BY_NAME' => {
+                        'admins' => [
+                            'CONFSITE_cbv4-pfds:/hostfile-cbv4-spawn'
+                            ]
+                        }
+                },
+                'coment'   => 'CBV4-PFDS root site',
+                'room'     => 'CBV4-PFDS Room Name',
+                'state'    => 'ROOT',
+                'dhcpvlan' => 'vlan-systeme',
+                'NETWORK'  => {
+                    'BY_TAG' => {
+                        '302' => 'vlan-admindsi',
+                        '39'  => 'vlan-pfds-int',
+                        '40'  => 'vlan-pfds-ext',
+                        '372' => 'vlan-middledsi',
+                        '13'  => 'vlan-systeme'
+                    },
+                    'BY_NAME' => {
+                        'vlan-admindsi' => {
+                            'network' => '10.3.1.0',
+                            'gateway' => '10.3.1.254',
+                            'tag'     => 302,
+                            'netmask' => '255.255.255.0',
+                            'scope'   => 'private'
+                        },
+                        'vlan-pfds-ext' => {
+                            'network' => '192.168.1.0',
+                            'comment' => 'something really interesting',
+                            'gateway' => '192.168.1.254',
+                            'tag'     => 40,
+                            'netmask' => '255.255.255.0',
+                            'scope'   => 'public'
+                        },
+                        'vlan-pfds-int' => {
+                            'network' => '10.2.0.0',
+                            'tag'     => 39,
+                            'netmask' => '255.255.0.0',
+                            'scope'   => 'private'
+                        },
+                        'vlan-systeme' => {
+                            'network' => '10.1.0.0',
+                            'tag'     => 13,
+                            'netmask' => '255.255.0.0',
+                            'scope'   => 'private'
+                        },
+                        'vlan-middledsi' => {
+                            'network' => '10.3.2.0',
+                            'gateway' => '10.3.2.254',
+                            'tag'     => 372,
+                            'netmask' => '255.255.255.0',
+                            'scope'   => 'private'
+                            }
+                    },
+                    'BY_ADDR' => {
+                        '192.168.1.0/24' => 'vlan-pfds-ext',
+                        '10.3.2.0/24'    => 'vlan-middledsi',
+                        '10.1.0.0/16'    => 'vlan-systeme',
+                        '10.3.1.0/24'    => 'vlan-admindsi',
+                        '10.2.0.0/16'    => 'vlan-pfds-int'
+                        }
+                },
+                'type' => 'site'
+            },
+            'cbv4' => {
+                'HOST' => {
+                    'BY_NAME' => {},
+                    'BY_ADDR' => {},
+                    'BY_MAC'  => {}
+                },
+                'location' => 'Courbevoie',
+                'zone'     => 'private',
+                'console'  => 'default',
+                'SERVICE'  => {
+                    'BY_NAME' => {}
+                },
+                'coment'   => 'CBV4 POP',
+                'room'     => 'CBV4 Room Name',
+                'state'    => 'EDGE',
+                'dhcpvlan' => 'vlan-systeme',
+                'NETWORK'  => {
+                    'BY_TAG' => {
+                        '13' => 'vlan-systeme'
+                    },
+                    'BY_NAME' => {
+                        'vlan-systeme' => {}
+                    },
+                    'BY_ADDR' => {
+                        '10.1.0.0/16' => 'vlan-systeme'
+                        }
+                },
+                'type' => 'site'
+                }
+        },
+        '__site_list' => [
+            'cbv4-pfds',
+            'cbv4'
+            ]
+    },
+    'ZONE' => {
+        'BY_NAME' => {
+            'private' => {
+                'BY_SITE' => {
+                    'cbv4-pfds' => {
+                        'vlan-admindsi' => {
+                            'broadcast' => 'A	10.3.1.255',
+                            'network'   => 'A	10.3.1.0',
+                            'gateway'   => 'A	10.3.1.254',
+                            'netmask'   => 'A	255.255.255.0'
+                        },
+                        'vip-spawn' => {
+                            'vip-spawn.vlan-pfds-ext' => 'A	192.168.1.99',
+                            'nscache'    => 'CNAME	vip-spawn.vlan-systeme',
+                            'vip-deploy' => 'CNAME	vip-spawn.vlan-systeme',
+                            'mf'         => 'CNAME	vip-spawn.vlan-systeme',
+                            'mirrors'    => 'CNAME	vip-spawn.vlan-systeme',
+                            'vip-spawn.vlan-systeme' => 'A	10.1.1.254',
+                            'nsprivate' => 'CNAME	vip-spawn.vlan-systeme',
+                            'cvs'       => 'CNAME	vip-spawn.vlan-systeme'
+                        },
+                        'cbv4-spawn' => {
+                            'cbv4-spawn01.vlan-middledsi' => 'A	10.3.2.42',
+                            'cbv4-spawn00.vlan-middledsi' => 'A	10.3.2.41',
+                            'spawn00' => 'CNAME	cbv4-spawn00.vlan-systeme',
+                            'cbv4-spawn01.vlan-pfds-ext' => 'A	192.168.1.98',
+                            'cbv4-spawn00.vlan-pfds-ext' => 'A	192.168.1.97',
+                            'cbv4-spawn01.vlan-admindsi' => 'A	10.3.1.42',
+                            'ntp00' => 'CNAME	cbv4-spawn00.vlan-systeme',
+                            'ntp'   => 'CNAME	cbv4-spawn.vlan-systeme',
+                            'ntp01' => 'CNAME	cbv4-spawn01.vlan-systeme',
+                            'cbv4-spawn00.vlan-admindsi' => 'A	10.3.1.41',
+                            'cbv4-spawn00.vlan-systeme'  => 'A	10.1.167.0',
+                            'cbv4-spawn' => 'CNAME	cbv4-spawn.vlan-systeme',
+                            'cbv4-spawn.vlan-systeme' => [
+                                'A	10.1.167.0',
+                                'A	10.1.167.1'
+                            ],
+                            'spawn01' => 'CNAME	cbv4-spawn01.vlan-systeme',
+                            'cbv4-spawn01.vlan-systeme' => 'A	10.1.167.1',
+                            'spawn' => 'CNAME	cbv4-spawn.vlan-systeme'
+                        },
+                        'cbv4-pfds-filer' => {
+                            'cbv4-pfds-filer00.vlan-systeme' => 'A	10.1.2.0',
+                            'cbv4-pfds-filer01.vlan-systeme' => 'A	10.1.2.1'
+                        },
+                        'vlan-systeme' => {
+                            'broadcast' => 'A	10.1.255.255',
+                            'network'   => 'A	10.1.0.0',
+                            'netmask'   => 'A	255.255.0.0'
+                        },
+                        'vlan-pfds-int' => {
+                            'broadcast' => 'A	10.2.255.255',
+                            'network'   => 'A	10.2.0.0',
+                            'netmask'   => 'A	255.255.0.0'
+                        },
+                        'vlan-pfds-ext' => {
+                            'broadcast' => 'A	192.168.1.255',
+                            'network'   => 'A	192.168.1.0',
+                            'gateway'   => 'A	192.168.1.254',
+                            'netmask'   => 'A	255.255.255.0'
+                        },
+                        'vlan-middledsi' => {
+                            'broadcast' => 'A	10.3.2.255',
+                            'network'   => 'A	10.3.2.0',
+                            'gateway'   => 'A	10.3.2.254',
+                            'netmask'   => 'A	255.255.255.0'
+                            }
+                    },
+                    'cbv4' => {
+                        'vlan-systeme' => {
+                            'broadcast' => 'A	10.1.255.255',
+                            'network'   => 'A	10.1.0.0',
+                            'netmask'   => 'A	255.255.0.0'
+                            }
+                        }
+                },
+                '__hostclass_order' => {
+                    'cbv4-pfds' => [
+                        'vip-spawn',
+                        'cbv4-pfds-filer',
+                        'cbv4-spawn'
+                        ]
+                },
+                'SOA' => {
+                    '@ns' => [
+                        'deploy00.vlan-systeme.private.',
+                        'deploy01.vlan-systeme.private.'
+                    ],
+                    'ttl'     => '1D      ; TTL (1 day)',
+                    'serial'  => 'AUTO',
+                    'console' => 'ttyS0,115200n8',
+                    '@mx'     => [
+                        '1       mf.private.',
+                        '2       mf00.private.',
+                        '2       mf01.private.'
+                    ],
+                    'retry'   => '1H      ; Retry (1 hour)',
+                    'negttl'  => '1H      ; Negative TTL (1 hours)',
+                    'comment' => 'Internal management zone',
+                    'refresh' => '6H      ; Refresh (6 hours)',
+                    'type'    => 'zone',
+                    'mail'    => 'dnsmaster at private',
+                    'soa'     => 'Deploy00.private.',
+                    'expire'  => '7D      ; Expire (7 days)'
+                },
+                '__network_order' => {
+                    'cbv4-pfds' => [
+                        'vlan-systeme',
+                        'vlan-pfds-int',
+                        'vlan-pfds-ext',
+                        'vlan-admindsi',
+                        'vlan-middledsi'
+                    ],
+                    'cbv4' => [
+                        'vlan-systeme'
+                        ]
+                    }
+                }
+        },
+        'BY_SITE' => {
+            'cbv4-pfds' => 'private',
+            'cbv4'      => 'private'
+            }
+        }
+};
+$expected_configuration->{'SITE'}{'BY_NAME'}{'cbv4'}{'NETWORK'}{'BY_NAME'}{'vlan-systeme'}
+    = $expected_configuration->{'SITE'}{'BY_NAME'}{'cbv4-pfds'}{'NETWORK'}{'BY_NAME'}
+    {'vlan-systeme'};
+
+is_deeply $parsed_configuration, $expected_configuration
+    => q{Returns the expected configuration hash}
+    or note explain $parsed_configuration;
+
+$parsed_configuration = Init_GLOBAL_NETCONFIG( q{COMMON:private-network}, $test_hash_subst );
+is_deeply $parsed_configuration, $expected_configuration
+    => q{Returns the expected configuration hash when no explicit $pf_config was given}
+    or note explain $parsed_configuration;
+
+
+########################################################################
+note('Testing PFTools::Structqueries::Get_hosttype_from_hostname');
+can_ok( 'PFTools::Structqueries', qw( Get_hosttype_from_hostname ) );
+
+my $global_config = $parsed_configuration;
+
+throws_ok { Get_hosttype_from_hostname() }
+    qr{ \A ERROR: [ ] Invalid [ ] empty [ ] or [ ] undefined [ ] [\$] hostname }xms
+    => 'Dies if no hostname';
+
+throws_ok { Get_hosttype_from_hostname( {} ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] hostname }xms
+    => q{Dies if non-scalar $hostname};
+
+throws_ok { Get_hosttype_from_hostname( 'hostname' ) }
+    qr{ \A ERROR: [ ] Invalid [ ] empty [ ] or [ ] undefined [ ] [\$] global_config }xms
+    => 'Dies if no global_config';
+
+throws_ok { Get_hosttype_from_hostname( 'hostname', 'global_config' ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
+    => q{Dies if non-hashref $global_config};
+
+# $site is optional
+lives_ok { Get_hosttype_from_hostname( 'hostname', {} ) }
+    'OK if no $site';
+
+throws_ok { Get_hosttype_from_hostname( 'hostname', {}, {} ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site }xms
+    => q{Dies if non-scalar $site};
+
+$parsed_configuration = Get_hosttype_from_hostname( 'cbv4-spawn00',
+    $global_config, 'cbv4-pfds' );
+is $parsed_configuration, q{cbv4-spawn}
+    => q{Returns the correct hosttype}
+    or diag explain $parsed_configuration;
+
+
+########################################################################
+note('Testing PFTools::Conf::Get_config_for_hostname_on_site');
+can_ok( 'PFTools::Conf', qw( Get_config_for_hostname_on_site ) );
+
+throws_ok { Get_config_for_hostname_on_site() }
+    qr{ \A ERROR: [ ] Invalid [ ] empty [ ] or [ ] undefined [ ] [\$] hostname }xms
+    => 'Dies if no hostname';
+
+throws_ok { Get_config_for_hostname_on_site( {} ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] hostname }xms
+    => q{Dies if non-scalar $hostname};
+
+throws_ok { Get_config_for_hostname_on_site( 'hostname' ) }
+    qr{ \A ERROR: [ ] Invalid [ ] empty [ ] or [ ] undefined [ ] [\$] site }xms
+    => 'Dies if no site';
+
+throws_ok { Get_config_for_hostname_on_site( 'hostname', {} ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-scalar [ ] [\$] site }xms
+    => q{Dies if non-scalar $site};
+
+throws_ok { Get_config_for_hostname_on_site( 'hostname', 'site', 'hash_subst' ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] hash_subst }xms
+    => q{Dies if non-hashref $hash_subst};
+
+throws_ok { Get_config_for_hostname_on_site( 'hostname', 'site', {}, 'global_config' ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] global_config }xms
+    => q{Dies if non-hashref $global_config};
+
+throws_ok { Get_config_for_hostname_on_site( 'hostname', 'site', {}, {}, 'pf_config' ) }
+    qr{ \A ERROR: [ ] Invalid [ ] non-hashref [ ] [\$] pf_config }xms
+    => q{Dies if non-hashref $pf_config};
+
+$parsed_configuration = Get_config_for_hostname_on_site(
+    'cbv4-spawn00', 'cbv4-pfds', $test_hash_subst, $global_config,
+    $test_pf_config
+);
+
+$expected_configuration = {
+    '/etc/apache2/sites-available/default' => {
+        'owner'  => 'root',
+        'source' => 'CONFIG:spawn%SECTIONNAME%',
+        'mode'   => '0644',
+        'filter' => 'filter_privateresolve %SOURCE% %HOSTNAME% %DESTINATION%',
+        'after_change' => '/etc/init.d/apache2 reload',
+        'on_noaction'  => 'true',
+        'group'        => 'root',
+        'action'       => 'addfile',
+        'depends'      => '/etc/apache2/ports.conf',
+    },
+    '/etc/apache2/ports.conf' => {
+        'on_noaction' => 'true',
+        'owner'       => 'root',
+        'source'      => 'CONFIG:spawn%SECTIONNAME%',
+        'group'       => 'root',
+        'mode'        => '0644',
+        'filter' => 'filter_privateresolve %SOURCE% %HOSTNAME% %DESTINATION%',
+        'action' => 'addfile',
+        'after_change' => '/etc/init.d/apache2 reload',
+    },
+    'pf-tools' => {
+        'action' => 'apt-get',
+        'after_change' =>
+            'echo "Re-run update-config"; killall -KILL update-config',
+    },
+    '/var/www/debian-backports' => {
+        'source'  => '/distrib/official-mirrors/debian-backports',
+        'action'  => 'addlink',
+        'depends' => '/etc/apache2/sites-available/default /distrib',
+    },
+    '/var/www/debian' => {
+        'source'  => '/distrib/official-mirrors/debian',
+        'action'  => 'addlink',
+        'depends' => '/etc/apache2/sites-available/default /distrib',
+    },
+    '/etc/apt/apt-ftp-ubuntu.conf' => {
+        'on_noaction' => 'true',
+        'owner'       => 'root',
+        'source'      => 'CONFIG:spawn%SECTIONNAME%',
+        'group'       => 'root',
+        'mode'        => '0640',
+        'action'      => 'addfile',
+        'after_change' =>
+            '[ -x /usr/local/sbin/update-apt-ftparchive ] && /usr/local/sbin/update-apt-ftparchive ubuntu',
+        'depends' => 'apt-utils',
+    },
+    '/var/lib/cvs/repository' => {
+        'source'  => 'cbv4-pfds-filer00.vlan-systeme.private:/vol/volvcs/cvs',
+        'options' => 'rw,nodev,tcp,nosuid,noexec,hard,intr,bg',
+        'action'  => 'addmount',
+        'fstype'  => 'nfs',
+    },
+    '__sections_order' => [
+        'pf-tools',
+        'apt-utils',
+        'bzip2',
+        '/etc/apt/apt-ftp-debian.conf',
+        '/etc/apt/apt-ftp-ubuntu.conf',
+        '/usr/local/sbin/update-apt-ftparchive',
+        '/distrib',
+        '/var/lib/cvs/repository',
+        '/etc/apache2/ports.conf',
+        '/etc/apache2/sites-available/default',
+        '/var/www/debian',
+        '/var/www/debian-security',
+        '/var/www/debian-custom',
+        '/var/www/debian-backports',
+        'dhcp',
+    ],
+    '/etc/apt/apt-ftp-debian.conf' => {
+        'on_noaction' => 'true',
+        'owner'       => 'root',
+        'source'      => 'CONFIG:spawn%SECTIONNAME%',
+        'group'       => 'root',
+        'mode'        => '0640',
+        'action'      => 'addfile',
+        'after_change' =>
+            '[ -x /usr/local/sbin/update-apt-ftparchive ] && /usr/local/sbin/update-apt-ftparchive debian',
+        'depends' => 'apt-utils',
+    },
+    'apt-utils' => {
+        'action' => 'apt-get',
+    },
+    '/usr/local/sbin/update-apt-ftparchive' => {
+        'on_noaction'  => '/usr/local/sbin/update-apt-ftparchive',
+        'owner'        => 'root',
+        'source'       => 'CONFIG:spawn%SECTIONNAME%',
+        'group'        => 'root',
+        'mode'         => '0750',
+        'action'       => 'addfile',
+        'after_change' => '/usr/local/sbin/update-apt-ftparchive',
+        'depends' =>
+            '/etc/apt/apt-ftp-debian.conf /etc/apt/apt-ftp-ubuntu.conf bzip2',
+    },
+    '/var/www/debian-custom' => {
+        'source'  => '/distrib/debian-custom',
+        'action'  => 'addlink',
+        'depends' => '/etc/apache2/sites-available/default /distrib',
+    },
+    'bzip2' => {
+        'action' => 'apt-get',
+    },
+    '/distrib' => {
+        'source' =>
+            'cbv4-pfds-filer00.vlan-systeme.private:/vol/volmirror/distrib',
+        'options' => 'rw,nodev,tcp,nosuid,hard,intr,bg',
+        'mode'    => '0750',
+        'action'  => 'addmount',
+        'fstype'  => 'nfs',
+    },
+    'dhcp' => {
+        'on_noaction' =>
+            'umask 022; mk_pxelinuxcfg GLOBAL:private-network /distrib/tftpboot/pxelinux.cfg/template',
+        'action' => 'actiongroup',
+        'after_change' =>
+            'umask 022; mk_pxelinuxcfg GLOBAL:private-network /distrib/tftpboot/pxelinux.cfg/template && /etc/init.d/dhcp3-server restart',
+    },
+    '/var/www/debian-security' => {
+        'source'  => '/distrib/official-mirrors/debian-security',
+        'action'  => 'addlink',
+        'depends' => '/etc/apache2/sites-available/default /distrib',
+    },
+};
+
+is_deeply $parsed_configuration, $expected_configuration
+    => q{Returns the expected configuration hash}
+    or note explain $parsed_configuration;
+
+
+
+#TODO: {
+#    local $TODO = 'Depends on other, still failing, tests';
+#} # END TODO
 
 remove_tree( q{/tmp/pf-test} );
 



More information about the pf-tools-commits mailing list