Retrouvez cet article dans : Linux Magazine 96
Introduction
Perl permet l’utilisation de nombreuses sources de données (bases de données relationnelles ou fichiers) grâce àPrésentation de CDBI
Naturellement,Pré-requis
Comme son nom l’indique (au moins, un peu, reconnaissez-le),Principe d’utilisation
Les exemples classiques tirés de la documentation de CREATE TABLE zone (
id INTEGER PRIMARY KEY,
zone VARCHAR(255),
internal INTEGER NOT NULL DEFAULT 0
);
Et une connexion package DNS::Manager::DBI; use base ‘Class::DBI’; DNS::Manager::DBI->connection(‘dbi:SQLite:dbname=dns.db’, ‘’, ‘’); package DNS::Manager::zone; use base ‘DNS::Manager::DBI’; DNS::Manager::zone->table(‘zone’); DNS::Manager::zone->columns(All => qw/id zone/);
Contexte de l’application exemple
Pour les administrateurs système qui gèrent des DNS, la fabrication des fichiers de zone (les bases de données à proprement parler) " à la main " peut s’avérer fastidieuse, voire périlleuse. Quoi de plus rageant qu’un caractère parasite introduit par une intervention humaine et indétectable pour qui n’est pas une machine ? Certes, il existe des outils pour valider la syntaxe des fichiers de zone. Néanmoins, certaines erreurs peuvent être très pénibles à détecter et corriger, si elles ne relèvent pas de la syntaxe. Fichiers de zone Les fichiers de zone constituent un format parfaitement défini (qui est aussi celui des requêtes DNS) et relativement lisible pour un sysadmin. Étant des fichiers texte, ils ne nécessitent qu’un simple éditeur de texte (vi, emacs, autre) pour être modifiés et peuvent facilement être gérés dans un SCM (SCCS, RCS, CVS, SVN, autre). Utiliser une base de données offre en effet un certain confort lorsqu’on est amené à gérer plusieurs centaines d’enregistrements, mais cela nécessite aussi de disposer d’une interface graphique ou web permettant de gérer les entrées. Par ailleurs, cela impose de repenser/recréer un système de gestion des versions. Nous envisageons donc une application avec un jeu de commandes s’appuyant sur une base de données assez simple. Cette dernière contient les correspondances A-Record (Nom<->IP) etSchéma
Les relations entre associations se traduisent par les références suivantes :ip -> machinemachine_zone -> zone, machinealias -> zone
CREATE TABLE machine (
id INTEGER PRIMARY KEY AUTOINCREMENT,
machine VARCHAR(50) UNIQUE NOT NULL DEFAULT ‘’,
visible INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE zone (
id INTEGER PRIMARY KEY AUTOINCREMENT,
zone VARCHAR(255) UNIQUE NOT NULL DEFAULT ‘’,
managed INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE alias (
alias VARCHAR(50) NOT NULL DEFAULT ‘’,
zone_alias INTEGER NOT NULL
REFERENCES zone(id),
destination VARCHAR(50) NOT NULL DEFAULT ‘’,
zone_destination INTEGER NOT NULL
REFERENCES zone(id),
recursive INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (alias,zone_alias,recursive)
);
CREATE TABLE ip (
ip VARCHAR(16) UNIQUE NOT NULL DEFAULT ‘’,
machine INTEGER NOT NULL
REFERENCES machine(id),
PRIMARY KEY (ip)
);
CREATE TABLE zone_machine (
zone INTEGER NOT NULL
REFERENCES zone(id),
machine INTEGER NOT NULL
REFERENCES machine(id),
PRIMARY KEY (zone,machine)
);
Quant à la table INSERT INTO zone(zone) VALUES (‘zone1’); INSERT INTO zone(zone) VALUES (‘zone2’); INSERT INTO zone(zone) VALUES (‘zone3’); INSERT INTO zone(zone) VALUES (‘zone4’);
Exemple de script
Un scénario assez simple consiste à afficher une liste de valeurs d’une table.dns-zone-list
Ce script utilise 1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 package DNS::Manager::DBI;
7 use base ‚Class::DBI‘;
8 DNS::Manager::DBI->connection(‚dbi:SQLite:dbname=dns.db‘, ‚‘, ‚‘);
9
10 package DNS::Manager::zone;
11 use base ‚DNS::Manager::DBI‘;
12 DNS::Manager::zone->table(‘zone’);
13 DNS::Manager::zone->columns(All => qw/id zone/);
14
15 my @zones = DNS::Manager::zone->retrieve_all;
16 foreach my $zone (@zones) {
17 print $zone->zone, “\n”;
18 }
Les lignes 6 à 8 permettent la connexion à la base de données.
Les lignes 10 à 13 permettent la création d’un objet 1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 package DNS::Manager::DBI;
7 use base ‚Class::DBI‘;
8 __PACKAGE__->connection(‚dbi:SQLite:dbname=dns.db‘, ‚‘, ‚‘);
9
10 package DNS::Manager::zone;
11 use base ‚DNS::Manager::DBI‘;
12 __PACKAGE__->table(‘zone’);
13 __PACKAGE__->columns(All => qw/id zone/);
14
15 my @zones = __PACKAGE__->retrieve_all;
16 foreach my $zone (@zones) {
17 print $zone->zone, “\n”;
18 }
Pour vérifier la syntaxe du script et l’exécuter :
% perl -c dns-zone-list dns-zone syntax OK % perl dns-zone-list zone1 zone2 zone3 zone4
dns-file-list-iterator
Si le nombre d’enregistrements dans une table est potentiellement élevé, vous auriez intérêt à utiliser des itérateurs. En effet, dans la version précédente, tous les enregistrements sont retournés en bloc, puis affichés un à un. En utilisant un itérateur, il suffit de demander l’enregistrement suivant à l’aide de la méthode #!/usr/bin/perl
use strict;
use warnings;
package DNS::Manager::DBI;
use base ‘Class::DBI’;
__PACKAGE__->connection(‘dbi:SQLite:dbname=dns.db’, ‘’, ‘’);
package DNS::Manager::zone;
use base ‘DNS::Manager::DBI’;
__PACKAGE__->table(‘zone’);
__PACKAGE__->columns(All => qw/id zone/);
my $iterator = __PACKAGE__->retrieve_all;
while (my $zone = $iterator->next) {
print $zone->zone, “\n”;
}
dns-zone (une version permettant l’ajout et la suppression)
Cette version du script s’appuie sur quelques principes assez simples. Tout d’abord, les déclarations du paquetage 1 #!/usr/bin/perl
2
3 # un commentaire pour Sonia
4
5 use strict;
6 use warnings;
7 use Getopt::Long;
8
9 use lib ‘lib’;
10 use DNS::Manager;
11 use DNS::Manager::Utils;
12
13 my %action = (
14 list => sub {
15 my $internal = shift;
16 foreach ( DNS::Manager::Zone->retrieve_all ) {
17 next if not $_->managed and not $internal;
18 print $_->zone;
19 print $_->managed ? ‘’ : ‘*’;
20 print "\n";
21 }
22 },
23
24 add => sub {
25 my ($zone,$internal) = @_;
26 if ( exists_zone($zone) ) {
27 warn “[$0] " $zone " existe déjà\n";
28 }
29 else {
30 print "[$0] ajout de " $zone "\n";
31 DNS::Manager::Zone->insert({
32 zone => $zone,
33 managed => ($internal) ? 0 : 1,
34 });
35 }
36 },
37
38 remove => sub {
39 my $zone = shift;
40 if ( exists_zone($zone) ) {
41 print “[$0] suppression de " $zone "\n";
42 DNS::Manager::Zone->search(zone=>$zone)->delete_all;
43 }
44 else {
45 warn "[$0] " $zone " n’existe pas\n";
46 }
47 },
48
49 );
50
51 my %zone;
52 my $internal;
53 GetOptions (
54 ‘internal’ => \$internal,
55 ‘add=s’ => \@{ $zone{add} },
56 ‘remove=s’ => \@{ $zone{remove} },
57 ‘list’ => sub { $action{list}->() },
58 ‘full-list’ => sub { $action{list}->(1) },
59 ‘help’ => sub { usage($0) },
60 );
61
62 for my $action (qw/add remove/) {
63 foreach ( split /,/, join ‘,’, @{ $zone{$action} } ) {
64 $action{$action}->($_,$internal);
65 }
66 }
La fonction sub exists_zone {
my $zone = shift;
DNS::Manager::Zone->retrieve(zone=>$zone) or return 0;
return 1;
}
La première action décrite concerne l’affichage d’une liste de zones lignes 15 à 23. La méthode% perl dns-zone --add zone5,zone6 --add zone7 --remove zone4,zone58 [dns-zone] ajout de " zone5 " [dns-zone] ajout de " zone6 " [dns-zone] ajout de " zone7 " [dns-zone] suppression de " zone4 " [dns-zone] " zone58 " n’existe pasCe script couvre les besoins élémentaires de gestion d’un objet
Description des relations entre tables
Prenons la relation qui existe entre " machine " et " ip ". Une machine peut disposer de plusieurs interfaces réseau, et donc avoir plusieurs adresses IP. Il est important de souligner qu’une adresse ne peut appartenir qu’à une machine. On a donc une relation 1-n entre " machine " et " ip ". Certes, dans des contextes de cluster de haute disponibilité ou dans un souci de répartition de charge, il est possible d’associer des adresses IP virtuelles et d’avoir une adresse du réseau partagée entre plusieurs machines. Néanmoins, le schéma est ainsi constitué, et il permet surtout d’illustrer les relations DNS::Manager::Machine->has_many(ips => ‘DNS::Manager::IP’);
DNS::Manager::IP->has_a(machine => ‘DNS::Manager::Machine’);
Cette écriture nous permet alors de manipuler des données dans la base de données aisément, sans écrire une ligne de SQL.
foreach my $machine ( DNS::Manager::Machine->retrieve_all ) {
print $machine->machine, "\n";
foreach my $ip ($machine->ips) {
print ‘ -> ‘, $ip->ip, "\n";
}
}
Le résultat de l’exécution du code ci-avant serait une liste des machines et, pour chacune, une liste de ses adresses IP.
machine1
-> 1.1.1.1
-> 1.1.1.2
machine2
-> 1.1.1.3
-> 1.1.1.4
Quelle que soit la méthode utilisée par my ($machine) = DNS::Manager::Machine->search(machine => ‘hephaistos’);
$machine->add_to_ips({ ip => ‘192.168.12.13’ });
La première ligne retourne une liste d’objets, ce qui justifie la présence de parenthèses autour de DNS::Manager::Machine->search(machine => ‘hephaistos’)->delete_all;Cette simple ligne permet d’effectuer une recherche dans la table
Comment modéliser les relations n-m ?
Si l’on se fie uniquement à la documentation du module, la réponse à cette question est : " On ne peut pas. Si vous le voulez, faites-le vous-même. " Mais si l’on fouille sur le wiki deDNS::Manager::Machine->has_many(zone_machines => ‘DNS::Manager::ZoneMachine’); DNS::Manager::ZoneMachine->has_a(machine => ‘DNS::Manager::Machine’); DNS::Manager::Zone->has_many(zones_machine => ‘DNS::Manager::ZoneMachine’); DNS::Manager::ZoneMachine->has_a(zone => ‘DNS::Manager::Zone’); DNS::Manager::Machine->has_many(zones => [‘DNS::Manager::ZoneMachine’ => ‘zone’]); DNS::Manager::Zone->has_many(machines => [‘DNS::Manager::ZoneMachine’ => ‘machine’]);Dans cette relation, trois tables sont en jeu :
my $machine = ‘machine1’;
my @zones = qw/ zone1 zone2 zone3 zone4 /;
my @ips = qw/ 1.1.1.1 1.1.1.2 1.1.1.3 /;
my $new_machine = DNS::Manager::Machine->insert({ machine => $machine });
foreach (@zones) {
my ($zone_obj) = DNS::Manager::Zone->search({zone=>$_});
$new_machine->add_to_zones({ zone => $zone_obj->id });
}
foreach (@ips) {
$new_machine->add_to_ips({ ip => $_ });
}
Dans cet exemple, la troisième ligne ajoute une entrée dans la table Comment traiter les schémas complexes ?
En particulier, comment modéliser plusieurs clefs étrangères composites dans une table référençant la même clef primaire d’une autre table ? Ce cas de figure n’est pas non plus très courant, mais si le schéma de votre base en comporte, voici une astuce tirée du wiki pour les gérer. Ici encore, on trouve beaucoup de sucre syntaxique. DNS::Manager::Zone->has_many(
zones_alias => [‘DNS::Manager::Alias’ => ‘zone_alias’],
‘zone_destination’
);
DNS::Manager::Zone->has_many(
zones_destination => [‘DNS::Manager::Alias’ => ‘zone_destination’],
‘zone_alias’
);
Il s’agit de faire entendre à CDBI qu’il y a une relation décrite à l’aide de la méthode Comment rechercher ?
L’information est recherchée pour les cas les plus courants avec la méthode my @machines = DNS::Manager::Machine->search(
machine => ‘machine1’,
visible => 1
);
my @zones = DNS::Manager::Zone->search_like(
zone => ‘z_n%’,
managed => 1,
{ order_by=>’id DESC, zone’ }
);
Si ces fonctions sont intéressantes et pratiques, elles restent limitées parce qu’elles ne s’appliquent qu’à une classe à la fois et donc à une seule table.
Nous verrons un peu plus loin comment bâtir des recherches qui ressemblent à des vraies requêtes ensemblistes, grâce au module Quelques modules supplémentaires pour se faciliter la vie
Détecter la structure d’une base avec Class::DBI::Loader
use Class::DBI::Loader;
my $loader = Class::DBI::Loader->new(
dsn => ‘dbi:SQLite:dbname=dns.db’,
namespace => ‘DNS::Manager’,
);
La différence la plus flagrante (outre l’absence de description très lourde) est qu’il suffit de fournir un nom pour la classe racine et tous les objets déductibles seront déduits et créés dans l’espace de nommage.
Le code s’en trouve considérablement allégé, mais au prix des deux conditions incontournables :
1. Le SGBD doit être supporté par Rechercher un peu plus finement avec Class::DBI::AbstractSearch
Autant l’avouer tout de suite, use Class::DBI::AbstractSearch;
my @machines = DNS::Manager::Machine->search_where(
machine => [ ‘hephaistos’, ‘odin’ ],
visible => { ‘!=’, 0 },
);
my @machines = DNS::Manager::Machine->search_where(
{
machine => [ ‘%o%’ ],
visible => { ‘!=’, 0 },
},
{
logic => ‘AND’,
order_by => "machine DESC",
limit_dialect => ‘LimitOffset’,
limit => 10,
offset => 20,
},
);
Utiliser du SQL tout de même ? C’est possible !
Supposons que l’on souhaite identifier 3 zones qui contiennent moins de 5 enregistrements DNS::Manager::Zone->columns(TEMP => qw/nb_machines/);
DNS::Manager::Zone->set_sql(small_zones => q{
SELECT zone.id, COUNT(zone_machine.machine) AS nb_machines
FROM zone, zone_machine
WHERE zone.id = zone_machine.zone
GROUP BY zone.id
HAVING nb_machines <= 5
LIMIT 3
});
foreach my $zone (DNS::Manager::Zone->search_small_zones) {
printf “%s : %d machine(s)\n”,
$zone->zone, scalar $zone->nb_machines;
}
La première ligne précise que la colonne % perl dns-zone-list-small-zones zone1 : 5 machine(s) zone2 : 1 machine(s) zone3 : 1 machine(s)Si vous préférez utiliser les connexions DBI et/ou si vous ne souhaitez pas déclarer des colonnes temporaires, c’est bien entendu possible. C’est un peu moins sexy et cela oblige à modifier la requête SQL pour obtenir le même résultat. En effet, alors qu’avec
DNS::Manager::DBI->set_sql(small_zones => q{
SELECT zone.zone, COUNT(zone_machine.machine) AS nb_machines
FROM zone, zone_machine
WHERE zone.id = zone_machine.zone
GROUP BY zone.id
HAVING nb_machines <= 5
LIMIT 3
});
my $sth = DNS::Manager::DBI->sql_small_zones;
$sth->execute();
while (my $row = $sth->fetch) {
my $zone = $row->[0];
my $nb_machines = $row->[1];
printf "%20s : %d machines\n",
$zone, scalar $nb_machines;
}
On peut aussi utiliser le mécanisme des placeholders (ou paramètres fictifs), qui permet de préparer une requête et de substituer des valeurs après une phase de précompilation. Dans ce cas, la requête SQL décrite à l’aide de Des déclencheurs (triggers) et des contraintes
Un déclencheur est un ensemble d’opérations réalisées à la survenue d’un événement. Ces événements sont très couramment des modifications (ajout, mise à jour, suppression) et les opérations sont réalisées au choix avant ou après l’événement en question. La documentation de CDBI révèle des informations précieuses sur la survenue des événements (par exemple, DNS::Manager::Machine->add_trigger(after_create => \&add_to_zone1);
sub add_to_zone1 {
my ($self,%args) = @_;
$self->add_to_zones({ zone => 1 });
}
Ce qui peut aussi s’écrire :
DNS::Manager::Machine->add_trigger(after_create => sub {
my ($self,%args) = @_;
$self->add_to_zones({ zone => 1 });
});
Dans la même veine, les contraintes procèdent de la même manière. Pour une classe donnée, une méthode Support des transactions
Les transactions ne sont pas en reste. Si vous ne souhaitez pas les implémenter par le biais de votre SGBD ou qu’elles ne sont pas disponibles, vous pouvez toujours en utiliser à travers sub do_transaction {
my $class = shift;
my ( $code ) = @_;
local $class->db_Main->{ AutoCommit };
eval { $code->() };
if ( $@ ) {
my $commit_error = $@;
eval { $class->dbi_rollback };
die $commit_error;
}
}
DNS::Manager::DBI->do_transaction( sub {
my $zone = DNS::Manager::Zone->insert(zone => ‘zone58’);
my $machine = DNS::Manager::Machine->insert({ machine => ‘machine58’ });
$machine->add_to_ips({ ip => ‘10.1.1.58’ });
$machine->add_to_zones({ zone => $zone->id });
} );
La transaction porte sur l’ajout d’une zone, d’une machine et, pour cette machine, l’ajout d’une adresse et le rattachement à la zone nouvellement créée.
Application pratique : génération de fichiers de zone pour le DNS
À partir des données dont nous disposons dans notre base de données, la fabrication de fichiers de zone (correspondances directes et inverses) est relativement aisée. Il existe quelques modules sur le CPAN pour faciliter l’écriture des en-têtes de fichiers de zone et leur contenu. Comme le format des données est relativement trivial, le code ci-après prend en charge directement l’écriture des enregistrements. Bien que le résultat soit fonctionnel, vous préférerez peut-être utiliser des modules existants pour remplir ces fonctions.Correspondances inverses
Pour les correspondances inverses, on met en regard une adresse réseau (IP) et un nom complet (FQDN). Pour ce type de requêtes DNS, les alias ne sont d’aucune utilité. Il nous faut obtenir la liste des machines et de leur adresse IP pour une zone donnée. La requête SQL qui fournit ces informations est la suivante : SELECT DISTINCT machine.machine AS src, ip.ip AS dst
FROM zone, zone_machine, machine, ip
WHERE zone.zone = ‘$zone’
AND zone.id = zone_machine.zone
AND zone_machine.machine = machine.id
AND machine.id = ip.machine
AND machine.visible = 1
Correspondances directes
Pour les correspondances directes, la requête DNS porte sur un nom de domaine complet (FQDN) et en réponse, le DNS fournit une adresse réseau avec, si nécessaire, les étapes de résolution des SELECT CASE
WHEN recursive = 1
THEN ‘*.’ || alias
ELSE alias
END AS src,
destination AS dst
FROM alias, zone z1, zone z2
WHERE z1.zone = ‘$zone’
AND alias.zone_alias = z1.id
AND z2.zone = ‘$zone’
AND alias.zone_destination = z2.id
UNION
SELECT CASE
WHEN recursive = 1
THEN ‘*.’ || alias
ELSE alias
END AS src,
destination || ‘.’ || z2.zone || ‘.’ AS dst
FROM alias, zone z1, zone z2
WHERE z1.zone = ‘$zone’
AND alias.zone_alias = z1.id
AND z2.zone <> ‘$zone’
AND alias.zone_destination = z2.id
Elle est ajoutée à la précédente par un opérateur DNS::Manager::Alias->columns(TEMP => qw/src dst/); DNS::Manager::Alias->set_sql(for_dump => $dump_query); my @aliases = DNS::Manager::Alias->search_for_dump;Pour ne pas empiéter sur les colonnes réelles, il est nécessaire de nommer les colonnes retournées par la requête avec des noms différents. Sans cette précaution, aucune modification ne sera enregistrée dans la base lors des insertions ou mises à jour pour les colonnes qui apparaissent à la fois dans
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Regexp::Common;
use Fcntl ‘:flock’;
use Socket;
use lib ‘lib’;
use DNS::Manager;
use DNS::Manager::Utils;
my ($direct, $reverse, $output_file, $zone);
GetOptions (
‘direct’ => \$direct,
‘reverse’ => \$reverse,
‘output-file’ => \$output_file,
‘zone=s’ => \$zone,
‘help’ => sub { usage($0); exit 1 },
);
die “[$0] quelle zone ?\n” unless $zone;
die “[$0] zone non gérée\n" unless is_managed_zone($zone);
die "[$0] ni direct ni inverse ???\n" if !$reverse and !$direct;
my $zone_filename = ‘’;
my $query = make_alias_dump_query($zone, $direct);
DNS::Manager::Alias->set_sql(for_dump => $query);
my $result = ‘’;
foreach my $alias (DNS::Manager::Alias->search_for_dump) {
if ($direct) {
$result .= $alias->src . ‘ IN ‘;
$result .=
$alias->dst =~ /$RE{net}{IPv4}/ && inet_aton($alias->dst) ?
‘A ‘ . $alias->dst :
‘CNAME ‘ . $alias->dst ;
$result .= “\n”;
}
elsif ($reverse) {
my $reverse_ip = join ‘.’, (reverse(split /\./, $alias->dst));
$result .= sprintf(“%s.in-addr.arpa. IN PTR %s.%s.\n”,
$reverse_ip, $alias->src, $zone
);
}
}
$zone_filename =
$reverse ? ‘inverse.’ . $zone :
$direct ? ‘direct.’ . $zone :
‘___.’ . $zone;
open(my $lf, ">$zone_filename.lock")
or die “Impossible d’ouvrir un fichier verrou : $!\n”;
flock($lf, 2)
or die “Impossible de verrouiller : $!\n”;
my $serial = compute_serial($zone_filename);
$result = generate_header($serial, $zone) . $result ;
if ($output_file) {
open(my $of, "> $zone_filename.$serial")
or die “Impossible d’ouvrir le fichier $zone_filename.$serial : $!\n”;
print $of $result
or die “Impossible d’écrire dans le fichier $zone_filename.$serial : $!\n";
close($of)
or warn "Impossible de fermer dans le fichier $zone_filename.$serial : $!\n";
if (-f $zone_filename) {
unlink $zone_filename
or die “Impossible de supprimer le lien $zone_filename\n”;
}
symlink “$zone_filename.$serial”, $zone_filename
or die “Impossible de créer le lien $zone_filename\n";
} else {
print $result ;
}
END {
if ($zone_filename && -f "$zone_filename.lock") {
unlink "$zone_filename.lock"
}
}
Quelques fonctions non décrites ici sont appelées pour générer un numéro de série valide, comportant la date du jour et un incrément sur deux chiffres, ainsi qu’un en-tête de fichier de zone valide. Ces fonctions sont très largement inspirées de Perl for System Administrator [7].
La requête SQL est également générée à l’aide d’une fonction qui retranscrit à la virgule près ce qui a été décrit un peu plus tôt.
% perl dns-file --zone zone1 --direct ~/src/dnsmam
; Generated by dns-file. Arguments were: “--zone zone1 --direct”
; Don’t edit manually, it will be overwritten.
; This program was run by bifo (uid=756) at Sat Aug 19 19:04:35 2006.
$TTL 1d
@ IN SOA ns0.zone1. nsmaster.zone1. (
2006081900 ; serial
3h ; refresh
1h ; retry
1w ; expire
1h) ; TTL
;
IN NS ns0.zone1.
IN NS ns1.zone1.
;
machine1 IN A 10.1.1.1
machine2 IN A 10.1.1.2
machine3 IN A 10.1.1.3
machine4 IN A 10.1.1.4
machine5 IN A 10.1.1.5
machine6 IN A 10.1.1.6
externe IN CNAME autre.domaine.
*.externe-recursif IN CNAME autre.domaine.
service1 IN CNAME machine1
*.service1 IN CNAME service1
Les lignes introduites par " ; " sont des commentaires et sont ignorées.
Pour aller plus loin
Il serait aussi possible d’utiliser les informations relatives aux projets pour supporter les champsVérification des données produites
La distribution de BIND est assortie d’outils de vérification de fichiers de configuration et de fichiers de zones. Néanmoins, cette opération est fastidieuse lorsque l’on héberge de nombreuses zones. En effet, #!/usr/bin/perl
# Suggested (brilliantly) by William HOFFMANN
use strict;
use warnings;
use File::Slurp;
our $conf_file = ‘/etc/named.conf’;
our $sysconf_file = ‘/etc/sysconfig/named’;
our $check_conf_command = ‘/usr/sbin/named-checkconf’;
our $check_zone_command = ‘/usr/sbin/named-checkzone’;
my $sysconf_text = read_file($sysconf_file)
or die "Could not read ‘$sysconf_file’: $!";
# Find the chroot’s path in /etc/sysconfig
# Another way to do it would be to source the file and check the var in @ENV
my ($root_dir) = $sysconf_text =~ m/^ROOTDIR=[‘"]?([^’"]*)[‘"]?$/m;
$root_dir = ‘’ unless $root_dir;
chomp $root_dir;
print ‘Found a root directory: ‘, $root_dir, “\n”;
# Get the named.conf path and check it with named_checkconf
if ( -r “$root_dir/$conf_file” ) {
$conf_file = “$root_dir/$conf_file”;
}
else {
die “$root_dir/$conf_file is not readable: $!”;
}
# Variants for named-checkconf
# * -t root_dir
# * path/to/named.conf
my @check_command = qq/$check_conf_command $conf_file/;
run( \@check_command, $conf_file )
or die “Something went wrong during config file checking: $!\n”;
# If the conf is OK, get the zone list
my $conf_text = read_file($conf_file);
# get optionaly the directory in which the zonefiles are stored
# e.g. directory “/var/named”;
( my $zone_basedir ) = $conf_text =~ m/^[^#]*directory\s+”([^”]+)”/;
print “Found a root for zonefiles: ‘$zone_basedir’\n”
unless $zone_basedir =~ /^$/;
# For each zone,
# * get the filename for its datafile
# * check the zone file with named_checkzone
my @zonenames = $conf_text =~ m/zone\s+"([^"]+)"/xmsg;
my @zones =
$conf_text =~
m/(zone\s+"([^"]+)"\s*IN\s*\{(?:[^}]*|.*file\s*"[^"]+"\s*;.*?\1)\};)/xmsg;
my %file;
foreach my $index ( 0..$#zones-1 ) {
next if $index%2;
my $zonename = $zones[ $index + 1 ];
my ($zonefile) = $zones[ $index ] =~ m/^.*file\s*"([^"]+)".*$/gmsx;
$file{$zonename} = $zonefile;
}
foreach my $zone (sort keys %file) {
print ‘zone: ‘, $zone, ‘ file: ‘, $file{$zone}, “\n”;
my $zone_filename = “$root_dir/$zone_basedir/$file{$zone}”;
my @check_command = qq/$check_zone_command $zone $zone_filename/;
run( \@check_command, $file{$zone} );
}
sub run {
my ( $commandref, $tested ) = @_;
my @command = @$commandref;
system(@command);
if ( $? == -1 ) {
print "Failed to execute: $!\n";
return 0;
}
elsif ( $? & 127 ) {
printf "Child died with signal %d, %s coredump\n", ( $? & 127 ),
( $? & 128 ) ? ‘with’ : ‘without’;
return 0;
}
else {
my $return = $? >> 8;
if ( $return == 0 ) {
print "’$conf_file’ seems OK.\n";
}
else {
warn "’@command’ failed with code: $?";
return 0;
}
}
return 1;
}
La fonction 




Donnez votre avis
Vous devez avoir ouvert une session pour écrire un commentaire.