source: trunk/ddt/ddt @ 365

Last change on this file since 365 was 365, checked in by g7moreau, 6 years ago
  • Error in $mac variable (change_type)
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 121.4 KB
Line 
1#!/usr/bin/perl
2#
3# Copyright (C) 2006-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
4# License GNU GPL version 2 or later and Perl equivalent
5#
6# apt-get install perl-base perl-modules libyaml-syck-perl libnet-netmask-perl libreadonly-perl libfile-touch-perl libtext-table-perl libnetaddr-ip-perl
7
8package DDT::RE;
9
10use strict;
11#use warnings;
12
13use Readonly;
14
15Readonly our $MAC_ADDRESS  => qr{ (?: [0-9A-F]{2} :){5} [0-9A-F]{2} }xms;
16Readonly our $IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
17
18
19package main;
20
21use strict;
22#use warnings;
23use version; our $VERSION = version->declare('0.11.9');
24
25use Getopt::Long qw(GetOptions);
26#use YAML;
27use YAML::Syck;
28use Net::Netmask;
29use File::Touch;
30use File::Copy;
31use Socket;
32use Text::Table;
33use NetAddr::IP;
34
35my $command = shift @ARGV || 'help';
36
37my %cmd_db = (
38   'add-alias'          => \&cmd_add_alias,
39   'add-dhcp'           => \&cmd_add_dhcp,
40   'add-float'          => \&cmd_add_float,
41   'add-static'         => \&cmd_add_static,
42   'add-virtual'        => \&cmd_add_virtual,
43   'change-comment'     => \&cmd_change_comment,
44   'change-sector'      => \&cmd_change_sector,
45   'change-host'        => \&cmd_change_host,
46   'change-ip'          => \&cmd_change_ip,
47   'change-mac'         => \&cmd_change_mac,
48   'change-tag'         => \&cmd_change_tag,
49   'change-type'        => \&cmd_change_type,
50   'check-dns'          => \&cmd_check_dns,
51   'create-sector'      => \&cmd_create_sector,
52   'create-pool'        => \&cmd_create_pool,
53   'create-pxe'         => \&cmd_create_pxe,
54   'create-tag'         => \&cmd_create_tag,
55   'del-pc'             => \&cmd_del_pc,
56   'del-float'          => \&cmd_del_float,
57   'disable-pc'         => \&cmd_disable_pc,
58   'disable-float'      => \&cmd_disable_float,
59   'disable-pxe'        => \&cmd_disable_pxe,
60   'enable-pc'          => \&cmd_enable_pc,
61   'enable-float'       => \&cmd_enable_float,
62   'enable-pxe'         => \&cmd_enable_pxe,
63   'gen-dhcp-file'      => \&cmd_generate_dhcp_file,
64   'gen-dns-file'       => \&cmd_generate_dns_file,
65   'help'               => \&cmd_help,
66   'load-database'      => \&cmd_load_database,
67   'remove-pxe'         => \&cmd_remove_pxe,
68   'remove-tag'         => \&cmd_remove_tag,
69   'search-mac'         => \&cmd_search_mac,
70   'sector-add-ip'      => \&cmd_sector_add_ip,
71   'show'               => \&cmd_show_host,
72   'show-sector'        => \&cmd_show_sector,
73   'show-pool'          => \&cmd_show_pool,
74   'show-pxe'           => \&cmd_show_pxe,
75   'show-tag'           => \&cmd_show_tag,
76   'upgrade-db'         => \&cmd_upgrade_db,
77   'version'            => \&cmd_version,
78   );
79
80#-------------------------------------------------------------------------------
81
82my $CONFIG;
83
84my $xdg_config_home = $ENV{'XDG_CONFIG_HOME'} || "$ENV{'HOME'}/.config";
85$CONFIG = config_load("$xdg_config_home/ddt/config.yml") if -e "$xdg_config_home/ddt/config.yml";
86
87my $COMPUTER_BASENAME   = $CONFIG->{'database'}{'basename'} || 'ddt';
88my $COMPUTER_EXT        = $CONFIG->{'database'}{'ext'}      || 'db';
89
90my $FOLDER_APP          = $CONFIG->{'database'}{'folder'}   || '/var/lib/ddt';
91my $FOLDER_BACKUP       = $CONFIG->{'database'}{'backup'}   || "$FOLDER_APP/backup";
92my $FOLDER_GEN_DHCP     = $CONFIG->{'generate'}{'dhcp'}     || "$FOLDER_APP/dhcp";
93my $FOLDER_GEN_DNS      = $CONFIG->{'generate'}{'dns'}      || "$FOLDER_APP/dns";
94my $SCRIPT_UPDATE       = $CONFIG->{'script'}{'update'}     || '/usr/share/ddt/update-dhcp-server';
95
96my $COMPUTER_YAML       = "$FOLDER_APP/$COMPUTER_BASENAME.$COMPUTER_EXT";
97
98#-------------------------------------------------------------------------------
99
100mkdir $FOLDER_APP, 0755      if not -d $FOLDER_APP;
101mkdir $FOLDER_BACKUP, 0755   if not -d $FOLDER_BACKUP;
102mkdir $FOLDER_GEN_DHCP, 0755 if not -d $FOLDER_GEN_DHCP;
103mkdir $FOLDER_GEN_DNS, 0755  if not -d $FOLDER_GEN_DNS;
104
105if (defined $cmd_db{$command}) {
106   $cmd_db{$command}->(@ARGV);
107   }
108else {
109   print {*STDERR} "ddt: command $command not found\n\n";
110   $cmd_db{'help'}->();
111   exit 1;
112   }
113
114exit;
115
116#--------------------------------------------------------------------------------
117# LOAD SAVE section
118#--------------------------------------------------------------------------------
119
120sub config_load {
121   my $config_file = shift;
122
123   my $configdb = YAML::Syck::LoadFile($config_file);
124
125   return $configdb;
126   }
127
128#---------------------------------------------------------------
129# Load computer database
130
131sub ipamdb_load {
132   my $database_yaml = shift;
133
134   touch $database_yaml if not -e $database_yaml;
135   my $computer_db = YAML::Syck::LoadFile($database_yaml);
136
137   # add database version if not exist
138   if (not exists $computer_db->{'version'}) {
139      $computer_db->{'version'} = 1;
140      }
141
142   return $computer_db;
143   }
144
145#---------------------------------------------------------------
146# Save computer database
147
148sub ipamdb_save {
149   my ($database_yaml, $computer_db) = @_;
150
151   my $dirdb = $database_yaml;
152      $dirdb =~ s{ / [^/]* $}{}xms;
153   mkdir "$dirdb", 0755 unless -d "$dirdb";
154   YAML::Syck::DumpFile($database_yaml, $computer_db);
155
156   return $computer_db;
157   }
158
159#--------------------------------------------------------------------------------
160# CONTROL section
161#--------------------------------------------------------------------------------
162
163sub control_exist_pool {
164   my ($computer_db, $pool) = @_;
165
166   return exists $computer_db->{'pool'}{$pool} ? 1 : 0;
167   }
168
169#-------------------------------------------------------------------------------
170#Nom: control_exist_sector
171#Description: controle l'existence d'un sector dans le fichier YAML
172#             return 0 (faux) ou 1 (vrai)
173
174sub control_exist_sector {
175   my ($computer_db, $sector) = @_;
176
177   return 1 if exists $computer_db->{$sector};
178
179   print {*STDERR} "Error: sector $sector not found\n";
180   return 0;
181   }
182
183#-------------------------------------------------------------------------------
184#Nom: control_exist_hostname
185#Description: controle l'existence d'un nom de machine dans le fichier YAML
186#             return 0 (si trouvé) ou 1 (si non trouvé)
187
188sub control_exist_hostname {
189   my ($computer_db, $sector, $hostname) = @_;
190
191   if ($computer_db->{$sector} eq '') {
192      return 1;
193      }
194
195   my @sectordb = @{$computer_db->{$sector}};
196
197   for my $computer (@sectordb) {
198      my ($mac_address, $attribute) = %{$computer};
199      return 0 if $attribute->{'hostname'} eq $hostname;
200      }
201   return 1;
202   }
203
204#-------------------------------------------------------------------------------
205#Nom: control_exist_mac
206#Description: controle l'existence d'une adresse MAC dans le fichier YAML
207#             return 0 (si trouvé) ou 1 (si non trouvé)
208
209sub control_exist_mac {
210   my ($computer_db, $mac) = @_;
211
212   for my $sector_current (keys %{$computer_db}) {
213      next if $sector_current eq 'dset';
214      next if $sector_current eq 'pool';
215      next if $sector_current eq 'pxe';
216      next if $sector_current eq 'tag';
217      next if $sector_current eq 'version';
218
219      my @sectordb = @{$computer_db->{$sector_current}};
220
221      LOOP_ON_COMPUTER:
222      for my $computer (@sectordb) {
223         my ($mac_address, $attribute) = %{$computer};
224         return 0 if $mac_address eq $mac;
225         }
226      }
227   return 1;
228   }
229
230#-------------------------------------------------------------------------------
231#Nom: control_exist_ip
232#Description: controle l'existence d'une adresse IP dans le fichier YAML
233#             return 0 (si trouvé) ou 1 (si non trouvé)
234
235sub control_exist_ip {
236   my ($computer_db, $ip) = @_;
237
238   for my $sector_current (keys %{$computer_db}) {
239      next if $sector_current eq 'dset';
240      next if $sector_current eq 'pool';
241      next if $sector_current eq 'pxe';
242      next if $sector_current eq 'tag';
243      next if $sector_current eq 'version';
244
245      LOOP_ON_COMPUTER:
246      for my $computer (@{$computer_db->{$sector_current}}) {
247         my ($mac_address, $attribute) = %{$computer};
248         #print "Erreur: cette adresse IP $ip existe déjà\n";
249         return 0 if $attribute->{'ip'} eq $ip;
250         }
251      }
252
253   for my $current_pool (keys %{$computer_db->{'pool'}}) {
254      #--- Cette partie pour tester les ip des pools est bonne ne plus la changer ---#
255      my @T_pool_ip = @{$computer_db->{'pool'}{$current_pool}{'ip'}};
256
257      for my $pool_ip (@T_pool_ip) {
258         #print "Erreur: cette adresse IP $ip existe déjà\n";
259         return 0 if $pool_ip eq $ip;
260         }
261      }
262
263   return 1;
264   }
265
266#-------------------------------------------------------------------------------
267
268sub control_ip_in_range {
269   my ($computer_db, $sector, $ip) = @_;
270
271   return 1 if not exists $computer_db->{'dset'}{$sector}{'ip_range'}; # No IP range defined for this sector
272
273   my $ip_addr = NetAddr::IP->new($ip);
274
275   LOOP_ON_IP_RANGE:
276   for my $ip_range_current (@{$computer_db->{'dset'}{$sector}{'ip_range'}}) {
277      my $range = NetAddr::IP->new($ip_range_current);
278      return 1 if $range->contains($ip_addr);
279      }
280
281   return 0;
282   }
283
284#-------------------------------------------------------------------------------------
285#Nom: control_syntaxe_mac
286#Description: controle la syntaxe d'une adresse MAC (juste la longueur pas les valeurs)
287#             return 0 (si trouvé) ou 1 (si non trouvé)
288
289sub control_syntax_mac_address {
290   my $mac = shift;
291
292   if (scalar(split /:/, $mac) == 6 and $mac =~ $DDT::RE::MAC_ADDRESS) {
293      return 1;
294      }
295
296   print {*STDERR} "Error: bad MAC syntax: $mac\n";
297   return 0;
298   }
299
300#-------------------------------------------------------------------------------------
301#Nom: control_syntax_ip
302#Description: controle la syntaxe d'une adresse IP (juste la longueur pas les valeurs)
303#             return 0 (si trouvé) ou 1 (si non trouvé)
304
305sub control_syntax_ip {
306   my $ip = shift;
307
308   return 1 if $ip ne 'pool';
309
310   return 0 if $ip !~ m{^(\d+\.){3}\d+$};
311   return 0 if not NetAddr::IP->new("$ip/32");
312   return 1;
313   }
314
315#-------------------------------------------------------------------------------------
316
317sub control_syntax_cidr {
318   my $cidr = shift;
319
320   return 0 if $cidr !~ m{^(\d+\.){3}\d+/\d+$};
321   return 0 if not NetAddr::IP->new($cidr);
322   return 1;
323   }
324
325#-------------------------------------------------------------------------------------
326
327sub control_syntax_comment {
328   my $comment = shift;
329
330   if ($comment !~ m{^20\d\d-\d\d-\d\d\s}) {
331      print {*STDERR} "Error: no date like 2014-01-10 at the beginning: $comment\n";
332      return 0;
333      }
334
335   if ($comment !~ m{\(\w+\)$}) {
336      print {*STDERR} "Error: no (SERVICE) at the end: $comment\n";
337      return 0;
338      }
339
340   if ($comment =~ m{\s\s}) {
341      print {*STDERR} "Error: double space: $comment\n";
342      return 0;
343      }
344   return 1;
345   }
346
347#--------------------------------------------------------------------------------
348# UTILITY section
349#--------------------------------------------------------------------------------
350
351sub get_cmd_name {
352   my ($pkg, $sub) = split /::/, (caller(1))[3];
353   $sub =~ s/^cmd_//;
354   $sub =~ s/_/-/g;
355   return $sub;
356   }
357
358#-------------------------------------------------------------------------------
359
360sub normalize_mac_address {
361   my $mac_address = shift;
362
363   # D07E-28D1-7AB8 or d07e28-d17ab8
364   if ($mac_address =~ m{^ (?: [0-9A-Fa-f]{4} -){2} [0-9A-Fa-f]{4} $}xms
365      or $mac_address =~ m{^ [0-9A-Fa-f]{6} - [0-9A-Fa-f]{6} $}xms) {
366      $mac_address =~ s/-//g;
367      return join q{:}, unpack('(A2)*', uc($mac_address));
368      }
369
370   return join q{:}, map { substr( uc("00$_"), -2) } split m/ [:-] /xms, $mac_address;
371   }
372
373#-------------------------------------------------------------------------------
374
375sub normalize_comment {
376   my $comment = shift;
377
378   $comment =~ s{^(20\d\d)/(\d\d)/(\d\d)\s(.*)$}{$1-$2-$3 $4};
379
380   return $comment;
381   }
382
383#--------------------------------------------------------------------------------
384
385sub get_mac_from_hostname {
386   my ($computer_db, $sector, $hostname, $mac) = @_;
387
388   return $mac if $mac ne '';
389   return ''   if $hostname eq '';
390
391   LOOP_ON_COMPUTER:
392   for my $computer (@{$computer_db->{$sector}}) {
393      my ($mac_address, $attribute) = %{$computer};
394
395      next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
396
397      return $mac_address;
398      }
399   }
400
401#--------------------------------------------------------------------------------
402
403sub get_mac_from_ip {
404   my ($computer_db, $sector, $ip, $mac) = @_;
405
406   return $mac if $mac ne '';
407   return ''   if $ip eq '';
408
409   LOOP_ON_COMPUTER:
410   for my $computer (@{$computer_db->{$sector}}) {
411      my ($mac_address, $attribute) = %{$computer};
412
413      next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
414      return $mac_address;
415      }
416   }
417
418#--------------------------------------------------------------------------------
419# return a tuple (hash computer, iostat)
420# iostat 0/ok, 1/not exist
421
422sub get_computer_from_mac {
423   my ($computer_db, $sector, $mac) = @_;
424
425   LOOP_ON_COMPUTER:
426   for my $computer (@{$computer_db->{$sector}}) {
427      my ($mac_address, $attribute) = %{$computer};
428
429      next LOOP_ON_COMPUTER if $mac_address ne $mac;
430
431      return $attribute, 0;
432      }
433   return {}, 1;
434   }
435
436#-------------------------------------------------------------------------------
437# ADD computer section
438#-------------------------------------------------------------------------------
439
440#-------------------------------------------------------------------------------
441#Nom: add_alias
442#Description: ajoute un alias pour une machine. Pour la fonctionnalité CNAME dans le DNS.
443
444sub add_alias {
445   my ($computer_db, $hostname, $sector, $alias) = @_;
446
447   control_exist_sector($computer_db, $sector) or exit;
448   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";
449
450   my @sectordb = @{$computer_db->{$sector}};
451
452   LOOP_ON_COMPUTER:
453   for my $computer (@sectordb) {
454      my ($mac_address, $attribute) = %{$computer};
455
456      next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
457
458      $alias .= ' ' . $attribute->{'alias'};
459      $attribute->{'alias'}       = $alias;
460      $attribute->{'modify_time'} = time;
461      ipamdb_save("$COMPUTER_YAML", $computer_db);
462      print "Info: update attribute alias to $alias for host $hostname [OK]\n";
463      exit;
464      }
465   }
466
467#-------------------------------------------------------------------------------
468#Nom: add_static
469#Description: ajoute une machine non dhcp (donc à adressage fixe dans le fichier YAML)
470
471sub add_static {
472   my ($computer_db, $hostname, $sector, $ip, $mac, $comment) = @_;
473
474   $mac = normalize_mac_address($mac);
475   $comment = normalize_comment($comment);
476   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";
477   control_syntax_mac_address($mac)                         or exit;
478   control_exist_mac($computer_db, $mac)                    or die "Error: physical MAC address already exists: $mac\n";
479   control_syntax_ip($ip)                                   or die "Error: bad IP syntax $ip\n";
480   control_exist_ip($computer_db, $ip)                      or die "Error: IP $ip address already exist in sector $sector\n";
481   control_ip_in_range($computer_db, $sector, $ip)          or die "Error: IP $ip is not in sector $sector IP range.\n";
482   control_syntax_comment($comment)                         or exit;
483   my $timestamp = time;
484   push @{$computer_db->{$sector}}, { $mac => {
485      'hostname'     => $hostname,
486      'ip'           => $ip,
487      'address_type' => 'static',
488      'enabled'      => 'yes',
489      'create_time'  => $timestamp,
490      'modify_time'  => $timestamp,
491      'comment'      => $comment,
492      'alias'        =>  '',
493      }};
494   print "Info: add the host: $hostname, IP: $ip, MAC: $mac, sector: $sector [OK]\n";
495
496   ipamdb_save("$COMPUTER_YAML", $computer_db);
497   }
498
499
500#-------------------------------------------------------------------------------
501#Nom: add_dhcp
502#Description: section à corriger pour prendre en compte l'ajout d'une machine dans un pool dhcp
503#--- usage: ddt add-dhcp -s legi-sector03 -h meolpacif -m 00:18:F3:03:6F:66 -i 194.254.66.165
504
505sub add_dhcp {
506   my ($computer_db, $hostname, $sector, $ip, $mac, $comment) = @_;
507
508   my $timestamp = time;
509   $mac = normalize_mac_address($mac);
510   $comment = normalize_comment($comment);
511   control_exist_sector($computer_db, $sector)              or exit;
512   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";
513   control_syntax_mac_address($mac)                         or exit;
514   control_exist_mac($computer_db, $mac)                    or die "Error: physical MAC address already exists: $mac\n";
515   control_syntax_ip($ip)                                   or die "Error: bad IP syntax $ip\n";
516   control_exist_ip($computer_db, $ip)                      or die "Error: IP address already exist in sector $sector: $ip.\n";
517   control_ip_in_range($computer_db, $sector, $ip)          or die "Error: IP $ip is not in sector $sector IP range.\n";
518   control_syntax_comment($comment)                         or exit;
519
520   push @{$computer_db->{$sector}}, { $mac => {
521      'hostname'     => $hostname,
522      'ip'           => $ip,
523      'address_type' => 'dhcp',
524      'enabled'      => 'yes',
525      'create_time'  => $timestamp,
526      'modify_time'  => $timestamp,
527      'comment'      => $comment,
528      'alias'        => '',
529      }};
530   print "Add the computer: $hostname, IP: $ip, MAC: $mac, sector: $sector\n";
531
532   ipamdb_save("$COMPUTER_YAML", $computer_db);
533   }
534
535#-------------------------------------------------------------------------------
536#--- usage: ddt add-float -s legi-sector03 -h meolpacif -m 00:18:F3:03:6F:66 -i 194.254.66.165
537
538sub add_float {
539   my ($computer_db, $pool, $sector, $mac, $comment) = @_;
540
541   my $timestamp = time;
542   $mac = normalize_mac_address($mac);
543   $comment = normalize_comment($comment);
544   control_exist_sector($computer_db, $sector)  or exit;
545   control_syntax_mac_address($mac)                   or exit;
546   control_exist_mac($computer_db, $mac)              or die "Error: physical MAC address already exists: $mac\n";
547   control_exist_pool($computer_db, $pool)            or die "Error: the pool doesn't exists: $pool\n";
548   control_syntax_comment($comment)                   or exit;
549   push @{$computer_db->{$sector}}, { $mac => {
550      'hostname'     => $pool,
551      'ip'           => $pool,
552      'address_type' => 'pool-dhcp',
553      'enabled'      => 'yes',
554      'create_time'  => $timestamp,
555      'modify_time'  => $timestamp,
556      'comment'      => $comment,
557      }};
558   print "Info: add the computer in pool MAC: $mac, sector: $sector, Pool: $pool [OK]\n";
559
560   ipamdb_save("$COMPUTER_YAML", $computer_db);
561   }
562
563#-------------------------------------------------------------------------------
564# ADD computer section
565#-------------------------------------------------------------------------------
566
567sub cmd_add_alias {
568   local @ARGV = @_;
569
570   my $help = get_cmd_name();
571   my ($hostname, $sector, $alias);
572
573   GetOptions(
574      'hostname|h=s'    => \$hostname,
575      'sector|s|d=s'    => \$sector,
576      'alias|a=s'       => \$alias,
577      );
578
579   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
580   exit_on_error_option($help)
581      if $hostname   eq ''
582      or $sector  eq ''
583      or $alias      eq '';
584
585   my $computer_db = ipamdb_load($COMPUTER_YAML);
586   add_alias($computer_db, $hostname, $sector, $alias);
587   }
588
589#-------------------------------------------------------------------------------
590
591sub cmd_add_dhcp {
592   local @ARGV = @_;
593
594   my $help = get_cmd_name();
595   my ($hostname, $sector, $ip, $mac, $comment);
596
597   GetOptions(
598      'hostname|h=s'    => \$hostname,
599      'sector|s|d=s'    => \$sector,
600      'ip|i=s'          => \$ip,
601      'mac|m=s'         => \$mac,
602      'comment|c=s'     => \$comment,
603      );
604
605   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
606   exit_on_error_option($help)
607      if $hostname   eq ''
608      or $sector  eq ''
609      or $ip         eq ''
610      or $mac        eq ''
611      or $comment    eq '';
612
613   my $computer_db = ipamdb_load($COMPUTER_YAML);
614   add_dhcp($computer_db, $hostname, $sector, $ip, $mac, $comment);
615   }
616
617#-------------------------------------------------------------------------------
618
619sub cmd_add_float {
620   local @ARGV = @_;
621
622   my $help = get_cmd_name();
623   my ($pool, $sector, $mac, $comment);
624
625   GetOptions(
626      'pool|p=s'        => \$pool,
627      'sector|s|d=s'    => \$sector,
628      'mac|m=s'         => \$mac,
629      'comment|c=s'     => \$comment,
630      );
631
632   ($pool, $sector) = split /\./, $pool, 2 if $pool =~ m/\./;
633   exit_on_error_option($help)
634      if $pool       eq ''
635      or $sector  eq ''
636      or $mac        eq ''
637      or $comment    eq '';
638
639   my $computer_db = ipamdb_load($COMPUTER_YAML);
640   add_float($computer_db, $pool, $sector, $mac, $comment);
641   }
642
643#-------------------------------------------------------------------------------
644
645sub cmd_add_static {
646   local @ARGV = @_;
647
648   my $help = get_cmd_name();
649   my ($hostname, $sector, $ip, $mac, $comment);
650
651   GetOptions(
652      'hostname|h=s'    => \$hostname,
653      'sector|s|d=s'    => \$sector,
654      'ip|i=s'          => \$ip,
655      'mac|m=s'         => \$mac,
656      'comment|c=s'     => \$comment,
657      );
658
659   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
660   exit_on_error_option($help)
661      if $hostname   eq ''
662      or $sector  eq ''
663      or $ip         eq ''
664      or $mac        eq ''
665      or $comment    eq '';
666
667   my $computer_db = ipamdb_load($COMPUTER_YAML);
668   add_static($computer_db, $hostname, $sector, $ip, $mac, $comment);
669   }
670
671#-------------------------------------------------------------------------------
672# No real computer, just an entry A in DNS with virtual MAC
673
674sub cmd_add_virtual {
675   local @ARGV = @_;
676
677   my $help = get_cmd_name();
678   my ($hostname, $sector, $ip, $comment);
679
680   GetOptions(
681      'hostname|h=s'    => \$hostname,
682      'sector|s|d=s'    => \$sector,
683      'ip|i=s'          => \$ip,
684      'comment|c=s'     => \$comment,
685      );
686
687   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
688   exit_on_error_option($help)
689      if $hostname   eq ''
690      or $sector  eq ''
691      or $ip         eq ''
692      or $comment    eq '';
693
694   my $computer_db = ipamdb_load($COMPUTER_YAML);
695
696   $comment = normalize_comment($comment);
697   my $timestamp = time;
698
699   control_exist_sector($computer_db, $sector)              or exit;
700   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";
701   control_syntax_ip($ip)                                   or die "Error: bad IP syntax $ip\n";
702   control_exist_ip($computer_db, $ip)                      or die "Error: IP address already exist in sector $sector: $ip.\n";
703   control_ip_in_range($computer_db, $sector, $ip)          or die "Error: IP $ip is not in sector $sector IP range.\n";
704   control_syntax_comment($comment)                         or exit;
705
706   my $mac = join ':', 'FF', 'FF', map({sprintf("%02X", $_)} split(/\./, $ip));
707   control_syntax_mac_address($mac)             or exit;
708   control_exist_mac($computer_db, $mac)         or die "Error: virtual physical MAC address already exists: $mac\n";
709
710   push @{$computer_db->{$sector}}, { $mac => {
711      'hostname'     => $hostname,
712      'ip'           => $ip,
713      'address_type' => 'static',
714      'enabled'      => 'yes',
715      'create_time'  => $timestamp,
716      'modify_time'  => $timestamp,
717      'comment'      => $comment,
718      }};
719   print "Add the virtual computer: $hostname, IP: $ip, sector: $sector\n";
720
721   ipamdb_save("$COMPUTER_YAML", $computer_db);
722   }
723
724#-------------------------------------------------------------------------------
725# CHANGE computer section
726#-------------------------------------------------------------------------------
727
728#-------------------------------------------------------------------------------
729#Nom: change_mac
730#Description: change la mac adresse d'une machine en saisissant soit l'ip
731#             soit le nom de la mahcine et spécifiant le domaine
732#--- usage: ddt change-mac -s legi-sector03 -h meolpacif -m 00:18:F3:03:6F:66
733#--- usage: ddt change-mac -s legi-sector03 -i 194.254.66.187 -m 00:18:F3:03:6F:66
734
735sub change_mac {
736   my ($hostname, $sector, $ip, $mac) = @_;
737
738   my $computer_db = ipamdb_load($COMPUTER_YAML);
739
740   $mac = normalize_mac_address($mac);
741   control_exist_sector($computer_db, $sector)  or exit;
742   control_syntax_mac_address($mac)                   or exit;
743   control_exist_mac($computer_db, $mac)              or die "Error: physical MAC address already exists: $mac\n";
744   if ($ip ne '') {
745      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";
746      if ( control_exist_ip($computer_db, $ip) == 1 ) {
747         print "Error: unkown IP address: $ip\n";
748         exit;
749         }
750      my @sectordb = @{$computer_db->{$sector}};
751      LOOP_ON_COMPUTER:
752      for my $computer (@sectordb) {
753         my ($mac_address, $attribute) = %{$computer};
754         die "Error: physical MAC address $mac already exists in sector $sector\n" if $mac_address eq $mac;
755
756         next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
757
758         $attribute->{'modify_time'} = time;
759         $computer->{$mac} = $attribute;     # add new mac
760         delete $computer->{$mac_address};   # remove old mac
761
762         ipamdb_save("$COMPUTER_YAML", $computer_db);
763         print "Info: update host $attribute->{'hostname'}, sector $sector, MAC $mac, IP $attribute->{'ip'} [OK]\n";
764         exit;
765         }
766      }
767   elsif ($hostname ne '') {
768      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
769         die "Error: unkown host $hostname, sector $sector\n";
770         }
771      my @sectordb = @{$computer_db->{$sector}};
772      LOOP_ON_COMPUTER:
773      for my $computer (@sectordb) {
774         my ($mac_address, $attribute) = %{$computer};
775         die "Error: physical MAC address $mac already exists in sector $sector\n" if $mac_address eq $mac;
776
777         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
778
779         $attribute->{'modify_time'} = time;
780         $computer->{$mac} = $attribute;     # add new mac
781         delete $computer->{$mac_address};   # remove old mac
782
783         ipamdb_save("$COMPUTER_YAML", $computer_db);
784         print "Info: update host $attribute->{'hostname'}, sector $sector, MAC $mac, IP $attribute->{'ip'} [OK]\n";
785         exit;
786         }
787      }
788   }
789
790#-------------------------------------------------------------------------------
791#Nom: change_ip
792#Description: change l'adresse IP d'une machine en saisissant le nom de la machine
793#             et le domaine
794
795sub change_ip {
796   my ($hostname, $sector, $ip) = @_;
797
798   my $computer_db = ipamdb_load($COMPUTER_YAML);
799
800   control_exist_sector($computer_db, $sector) or exit;
801   if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
802      die "Error: unkown host: $hostname, in sector: $sector\n";
803      }
804   control_syntax_ip($ip)                          or die "Error: bad IP syntax $ip\n";
805   control_exist_ip($computer_db, $ip)             or die "Error: IP $ip address already exist in sector $sector\n";
806   control_ip_in_range($computer_db, $sector, $ip) or die "Error: IP $ip is not in sector $sector IP range.\n";
807
808   my @sectordb = @{$computer_db->{$sector}};
809
810   LOOP_ON_COMPUTER:
811   for my $computer (@sectordb) {
812      my ($mac_address, $attribute) = %{$computer};
813     
814      next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
815 
816      if ($attribute->{'address_type'} eq 'pool-dhcp') {
817         die "Error: host $hostname from sector $sector belongs to a a pool [FAILED]" .
818            " ... use 'del-float' command before";
819         }
820
821      $attribute->{'modify_time'} = time;
822      $attribute->{'ip'}          = $ip;
823      ipamdb_save("$COMPUTER_YAML", $computer_db);
824      print "Info: update host $hostname MAC: $mac_address IP: $ip [OK]\n";
825      exit;
826      }
827   }
828
829#-------------------------------------------------------------------------------
830#Nom: change_host
831#Description: change le computer hostname en saisissant l'IP et le domaine
832
833sub change_host {
834   my ($hostname, $sector, $ip) = @_;
835
836   my $computer_db = ipamdb_load($COMPUTER_YAML);
837
838   control_exist_sector($computer_db, $sector) or exit;
839   control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";
840   if ( control_exist_ip($computer_db, $ip) == 1 ) {
841      die "Error: unkown IP address: $ip\n";
842      }
843   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";
844
845   my @sectordb = @{$computer_db->{$sector}};
846
847   LOOP_ON_COMPUTER:
848   for my $computer (@sectordb) {
849      my ($mac_address, $attribute) = %{$computer};
850
851      next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
852
853      $attribute->{'modify_time'} = time;
854      $attribute->{'hostname'}    = $hostname;
855      ipamdb_save("$COMPUTER_YAML", $computer_db);
856      print "Info: update host $hostname MAC: $mac_address IP: $ip [OK]\n";
857      exit;
858      }
859
860   die "Error: failed to update hostname $hostname [FAILED]\n" .
861      " ... no IP $ip belongs to the sector $sector\n";
862   }
863
864#--------------------------------------------------------------------------------
865
866sub cmd_change_mac {
867   local @ARGV = @_;
868
869   my $help = get_cmd_name();
870   my ($hostname, $sector, $ip, $mac);
871
872   GetOptions(
873      'hostname|h=s'    => \$hostname,
874      'sector|s|d=s'    => \$sector,
875      'ip|i=s'          => \$ip,
876      'mac|m=s'         => \$mac,
877      );
878
879   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
880   exit_on_error_option($help)
881      if $sector  eq ''
882      or $mac        eq '';
883   exit_on_error_option($help)
884      if $hostname   ne ''
885      and $ip        ne '';
886
887   change_mac($hostname, $sector, $ip, $mac);
888   }
889
890#--------------------------------------------------------------------------------
891
892sub cmd_change_ip {
893   local @ARGV = @_;
894
895   my $help = get_cmd_name();
896   my ($hostname, $sector, $ip);
897
898   GetOptions(
899      'hostname|h=s'    => \$hostname,
900      'sector|s|d=s'    => \$sector,
901      'ip|i=s'          => \$ip,
902      );
903
904   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
905   exit_on_error_option($help)
906      if $hostname   eq ''
907      or $sector  eq ''
908      or $ip         eq '';
909
910   change_ip($hostname, $sector, $ip);
911   }
912
913#--------------------------------------------------------------------------------
914
915sub cmd_change_host {
916   local @ARGV = @_;
917
918   my $help = get_cmd_name();
919   my ($hostname, $sector, $ip);
920
921   GetOptions(
922      'hostname|h=s'    => \$hostname,
923      'sector|s|d=s'    => \$sector,
924      'ip|i=s'          => \$ip,
925      );
926
927   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
928   exit_on_error_option($help)
929      if $hostname   eq ''
930      or $sector  eq ''
931      or $ip         eq '';
932
933   change_host($hostname, $sector, $ip);
934   }
935
936#--------------------------------------------------------------------------------
937
938sub cmd_change_comment {
939   local @ARGV = @_;
940
941   my $help = get_cmd_name();
942   my ($sector, $mac, $comment);
943
944   GetOptions(
945      'sector|s|d=s'    => \$sector,
946      'mac|m=s'         => \$mac,
947      'comment|c=s'     => \$comment,
948      );
949
950   exit_on_error_option($help)
951      if $sector  eq ''
952      or $mac        eq ''
953      or $comment    eq '';
954
955   $mac     = normalize_mac_address($mac);
956   $comment = normalize_comment($comment);
957
958   my $computer_db = ipamdb_load($COMPUTER_YAML);
959
960   control_exist_sector($computer_db, $sector)  or exit;
961   control_syntax_mac_address($mac)                   or exit;
962   control_syntax_comment($comment)                   or exit;
963
964   my @sectordb = @{$computer_db->{$sector}};
965
966   LOOP_ON_COMPUTER:
967   for my $computer (@sectordb) {
968      my ($mac_address, $attribute) = %{$computer};
969
970      next LOOP_ON_COMPUTER if $mac_address ne $mac;
971
972      $attribute->{'modify_time'} = time;
973      $attribute->{'comment'}     = $comment;
974      ipamdb_save("$COMPUTER_YAML", $computer_db);
975      exit;
976      }
977   die "Error : Host $mac comment [FAILED]\n" .
978      " ... No MAC: $mac belongs to the domaine set $sector.\n";
979   }
980
981#--------------------------------------------------------------------------------
982
983sub cmd_change_sector {
984   local @ARGV = @_;
985
986   my $help = get_cmd_name();
987   my ($sector, $ip, $mac);
988
989   GetOptions(
990      'sector|s|d=s'    => \$sector,
991      'ip|i=s'          => \$ip,
992      'mac|m=s'         => \$mac,
993      );
994
995   exit_on_error_option($help)
996      if $sector  eq ''
997      or $ip         eq ''
998      or $mac        eq '';
999
1000   $mac = normalize_mac_address($mac);
1001
1002   my $computer_db = ipamdb_load($COMPUTER_YAML);
1003
1004   control_exist_sector($computer_db, $sector)  or exit;
1005   control_syntax_ip($ip)                       or die "Error: bad IP syntax $ip\n";
1006   control_syntax_mac_address($mac)             or exit;
1007
1008   LOOP_ON_SECTOR:
1009   for my $sector_current (keys %{$computer_db}) {
1010      next if $sector_current eq 'dset';
1011      next if $sector_current eq 'pool';
1012      next if $sector_current eq 'pxe';
1013      next if $sector_current eq 'tag';
1014      next if $sector_current eq 'version';
1015
1016      my @sectordb = @{$computer_db->{$sector_current}};
1017      my $computer_index = 0;
1018      LOOP_ON_COMPUTER:
1019      for my $computer (@sectordb) {
1020         my ($mac_address, $attribute) = %{$computer};
1021
1022         $computer_index++, next LOOP_ON_COMPUTER if $mac_address ne $mac;
1023         next LOOP_ON_SECTOR if $attribute->{'ip'} ne $ip;
1024
1025         $attribute->{'modify_time'} = time;
1026         splice(@{$computer_db->{$sector_current}}, $computer_index => 1);
1027         push @{$computer_db->{$sector}}, { $mac => $attribute };
1028
1029         ipamdb_save("$COMPUTER_YAML", $computer_db);
1030         exit;
1031         }
1032      }
1033   die "Error: update of sector $sector [FAILED]\n" .
1034      " ... MAC $mac and IP $ip don't exists in the database\n";
1035   }
1036
1037#--------------------------------------------------------------------------------
1038
1039sub cmd_change_tag {
1040   local @ARGV = @_;
1041
1042   my $help = get_cmd_name();
1043   my ($hostname, $sector, $ip, $mac, $tags);
1044
1045   GetOptions(
1046      'hostname|h=s'    => \$hostname,
1047      'sector|s|d=s'    => \$sector,
1048      'ip|i=s'          => \$ip,
1049      'mac|m=s'         => \$mac,
1050      'tag|t=s'         => \$tags,
1051      );
1052
1053   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1054
1055   exit_on_error_option($help)
1056      if $sector     eq ''
1057      or $tags       eq '';
1058   exit_on_error_option($help)
1059      if $mac        eq ''
1060      and $hostname  eq ''
1061      and $ip        eq '';
1062
1063   $mac = normalize_mac_address($mac);
1064
1065   my $computer_db = ipamdb_load($COMPUTER_YAML);
1066
1067   if ($tags !~ m/^ (?:\w+,)* \w+ $/xms) {
1068      die "Error: bad format for tags (comma separated list): $tags\n";
1069      }
1070
1071   for my $tag (split/,/, $tags) {
1072      next if $tag eq 'universal';
1073      die "Error: TAG doesn't exist in the database. Create it before with create_tag: $tag\n" if not exists $computer_db->{'tag'}{$tag};
1074      }
1075
1076   control_exist_sector($computer_db, $sector) or exit;
1077
1078   $mac = get_mac_from_ip($computer_db, $sector, $ip, $mac)             if $ip ne '';
1079   $mac = get_mac_from_hostname($computer_db, $sector, $hostname, $mac) if $hostname ne '';
1080   control_syntax_mac_address($mac) or exit;
1081
1082   LOOP_ON_COMPUTER:
1083   for my $computer (@{$computer_db->{$sector}}) {
1084      my ($mac_address, $attribute) = %{$computer};
1085
1086      next LOOP_ON_COMPUTER if $mac_address ne $mac;
1087
1088      $attribute->{'tag'}         = $tags;
1089      $attribute->{'modify_time'} = time;
1090
1091      delete $attribute->{'tag'} if $tags eq 'universal';
1092      ipamdb_save("$COMPUTER_YAML", $computer_db);
1093      exit;
1094      }
1095   print "Mise à jour du commentaire de la machine [FAILED]\n";
1096   print "L'adresse MAC: $mac n'existe pas dans le domaine: $sector.\n";
1097   }
1098
1099#--------------------------------------------------------------------------------
1100
1101sub cmd_change_type {
1102   local @ARGV = @_;
1103
1104   my $help = get_cmd_name();
1105   my ($hostname, $sector, $type);
1106
1107   GetOptions(
1108      'hostname|h=s'    => \$hostname,
1109      'sector|s|d=s'    => \$sector,
1110      'type|t=s'        => \$type,
1111      );
1112
1113   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1114
1115   exit_on_error_option($help)
1116      if $sector     eq ''
1117      and $hostname  eq ''
1118      and $type      eq '';
1119
1120   my $computer_db = ipamdb_load($COMPUTER_YAML);
1121
1122   if ($type !~ m/^ (dhcp|float|static|virtual) $/xms) {
1123      die "Error: bad type: $type\n";
1124      }
1125
1126   $type =~ s/^float$/pool-dhcp/;
1127   # No type virtual actually. See and write exactly type name and their property
1128
1129   control_exist_sector($computer_db, $sector) or exit;
1130
1131   LOOP_ON_COMPUTER:
1132   for my $computer (@{$computer_db->{$sector}}) {
1133      my ($mac_address, $attribute) = %{$computer};
1134
1135      next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
1136
1137      die "Warning: host $hostname already of type $type\n" if $attribute->{'address_type'} eq $type;
1138
1139      $attribute->{'address_type'}  = $type;
1140      $attribute->{'modify_time'}   = time;
1141
1142      ipamdb_save("$COMPUTER_YAML", $computer_db);
1143      exit;
1144      }
1145   die "Error: change host $hostname type [FAILED]\n";
1146   }
1147
1148#-------------------------------------------------------------------------------
1149# ACTIVATION section
1150#-------------------------------------------------------------------------------
1151
1152#-------------------------------------------------------------------------------
1153#Nom: disable_pc
1154#Description: désactive une machine (du DHCP ou en IP statique, et du DNS) (champs enabled=non)
1155
1156sub disable_pc {
1157   my ($hostname, $sector, $ip) = @_;
1158
1159   my $computer_db = ipamdb_load($COMPUTER_YAML);
1160
1161   if ($ip ne '') { # disable by IP
1162      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
1163      if ( control_exist_ip($computer_db, $ip) == 1 ) {
1164         die "Error: unkown IP address: $ip [FAILED]\n";
1165         }
1166
1167      for my $sector_current (keys %{$computer_db}) {
1168         next if $sector_current eq 'dset';
1169         next if $sector_current eq 'pool';
1170         next if $sector_current eq 'pxe';
1171         next if $sector_current eq 'tag';
1172         next if $sector_current eq 'version';
1173
1174         my @sectordb = @{$computer_db->{$sector_current}};
1175         LOOP_ON_COMPUTER:
1176         for my $computer (@sectordb) {
1177            my ($mac_address, $attribute) = %{$computer};
1178
1179            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
1180
1181            if ($attribute->{'enabled'} eq 'no') {
1182               print "Info: IP $ip from sector $sector_current is already disable [OK]" .
1183                  " ... Status: $attribute->{'enabled'}\n";
1184               exit;
1185               }
1186
1187            my $timestamp = time;
1188            $attribute->{'modify_time'} = $timestamp;
1189            $attribute->{'enabled'}     = 'no';
1190            ipamdb_save("$COMPUTER_YAML", $computer_db);
1191            print "Info: disabling IP $ip from sector $sector_current [OK]" .
1192               " ... Status: $attribute->{'enabled'}\n";
1193            exit;
1194            }
1195         }
1196      }
1197   else { # disable by Hostname
1198      control_exist_sector($computer_db, $sector);
1199      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
1200         die "Error: unkown host: $hostname, in sector: $sector [FAILED]\n";
1201         }
1202
1203      LOOP_ON_COMPUTER:
1204      for my  $computer (@{$computer_db->{$sector}}) {
1205         my ($mac_address, $attribute) = %{$computer};
1206
1207         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
1208
1209         if ($attribute->{'address_type'} eq 'pool-dhcp') {
1210            die "Error: host $hostname from sector $sector belongs to a a pool [FAILED]" .
1211               " ... use 'disable-float' command instead";
1212            }
1213
1214         if ($attribute->{'enabled'} eq 'no') {
1215            print "Info: host $hostname from sector $sector is already disable [OK]" .
1216               " ... Status: $attribute->{'enabled'}\n";
1217            exit;
1218            }
1219
1220         my $timestamp = time;
1221         $attribute->{'modify_time'} = $timestamp;
1222         $attribute->{'enabled'}     = 'no';
1223         ipamdb_save("$COMPUTER_YAML", $computer_db);
1224         print "Info: disabling host $hostname from sector $sector [OK]" .
1225            " ... Status: $attribute->{'enabled'}\n";
1226         exit;
1227         }
1228      }
1229   }
1230
1231#-------------------------------------------------------------------------------
1232
1233sub disable_float {
1234   my ($pool, $mac) = @_;
1235
1236   my $computer_db = ipamdb_load($COMPUTER_YAML);
1237
1238   if ( control_exist_mac($computer_db, $mac) == 1 ) {
1239      die "Error: unkown physical MAC address: $mac [FAILED]\n";
1240      }
1241
1242   for my $sector_current (keys %{$computer_db}) {
1243      next if $sector_current eq 'dset';
1244      next if $sector_current eq 'pool';
1245      next if $sector_current eq 'pxe';
1246      next if $sector_current eq 'tag';
1247      next if $sector_current eq 'version';
1248
1249      my @sectordb = @{$computer_db->{$sector_current}};
1250
1251      LOOP_ON_COMPUTER:
1252      for my $computer (@sectordb) {
1253         my ($mac_address, $attribute) = %{$computer};
1254         next LOOP_ON_COMPUTER if $mac_address ne $mac;
1255
1256         if ($attribute->{'ip'} eq $pool) {
1257            if ($attribute->{'enabled'} eq 'no') {
1258               print "Info: host $mac from pool $pool is already disable [OK]" .
1259                  " ... Status: $attribute->{'enabled'}\n";
1260               exit;
1261               }
1262            my $timestamp = time;
1263            $attribute->{'modify_time'} = $timestamp;
1264            $attribute->{'enabled'}     = 'no';
1265            ipamdb_save("$COMPUTER_YAML", $computer_db);
1266            print "Info: disabling host $mac from pool $pool [OK]" .
1267               " ... Status: $attribute->{'enabled'}\n";
1268            exit;
1269            }
1270         else {
1271            die "Error: host disable $mac [FAILED]" .
1272               " ... The host $mac does not belong to the $pool pool.\n";
1273            }
1274         }
1275      }
1276   }
1277
1278#-------------------------------------------------------------------------------
1279#Nom: enable_pc
1280#Description: active une machine désactivée(du DHCP ou en IP statique, et du DNS) (champs enabled=non)
1281
1282sub enable_pc {
1283   my ($hostname, $sector, $ip) = @_;
1284
1285   my $computer_db = ipamdb_load($COMPUTER_YAML);
1286
1287   control_exist_sector($computer_db, $sector) or exit;
1288
1289   if ($ip ne '') { # enable by IP
1290      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
1291      if ( control_exist_ip($computer_db, $ip) == 1 ) {
1292         print "Error: unkown IP address: $ip\n";
1293         exit;
1294         }
1295
1296      for my $sector_current (keys %{$computer_db}) {
1297         next if $sector_current eq 'dset';
1298         next if $sector_current eq 'pool';
1299         next if $sector_current eq 'pxe';
1300         next if $sector_current eq 'tag';
1301         next if $sector_current eq 'version';
1302
1303         my @sectordb = @{$computer_db->{$sector_current}};
1304
1305         LOOP_ON_COMPUTER:
1306         for my $computer (@sectordb) {
1307            my ($mac_address, $attribute) = %{$computer};
1308            if ($attribute->{'ip'} eq $ip) {
1309
1310               if ($attribute->{'enabled'} eq 'yes') {
1311                  print "Info: IP $ip belongs to sector $sector is already enable [OK]" .
1312                     " ... Status: $attribute->{'enabled'}\n";
1313                  exit;
1314                  }
1315
1316               my $timestamp = time;
1317               $attribute->{'modify_time'} = $timestamp;
1318               $attribute->{'enabled'}     = 'yes';
1319               ipamdb_save("$COMPUTER_YAML", $computer_db);
1320               print "Info: IP $ip is now enable [OK]" .
1321                  " ... Status: $attribute->{'enabled'}\n";
1322               exit;
1323               }
1324            }
1325         }
1326      }
1327   else { # enable by Hostname
1328      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
1329         die "Error: unkown host: $hostname, in sector: $sector\n";
1330         }
1331
1332      LOOP_ON_COMPUTER:
1333      for my $computer (@{$computer_db->{$sector}}) {
1334         my ($mac_address, $attribute) = %{$computer};
1335         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
1336
1337         if ($attribute->{'address_type'} eq 'pool-dhcp') {
1338            die "Error: host $hostname from sector $sector belongs to a a pool [FAILED]" .
1339               " ... use 'enable-float' command instead";
1340            }
1341
1342         if ($attribute->{'enabled'} eq 'yes') {
1343            print "Info: host $hostname belongs to sector $sector is already enable [OK]" .
1344               " ... Status: $attribute->{'enabled'}\n";
1345            exit;
1346            }
1347
1348         my $timestamp = time;
1349         $attribute->{'modify_time'} = $timestamp;
1350         $attribute->{'enabled'}     = 'yes';
1351         ipamdb_save("$COMPUTER_YAML", $computer_db);
1352         print "Info: host $hostname is now enable [OK]" .
1353            " ... Status: $attribute->{'enabled'}\n";
1354         exit;
1355         }
1356      }
1357   }
1358
1359#-------------------------------------------------------------------------------
1360
1361sub enable_float {
1362   my ($pool, $mac) = @_;
1363
1364   my $computer_db = ipamdb_load($COMPUTER_YAML);
1365
1366   if ( control_exist_mac($computer_db, $mac) == 1 ) {
1367      die "Error: unkown physical MAC address: $mac [FAILED]\n";
1368      }
1369
1370   for my $sector_current (keys %{$computer_db}) {
1371      next if $sector_current eq 'dset';
1372      next if $sector_current eq 'pool';
1373      next if $sector_current eq 'pxe';
1374      next if $sector_current eq 'tag';
1375      next if $sector_current eq 'version';
1376
1377      my @sectordb = @{$computer_db->{$sector_current}};
1378
1379      LOOP_ON_COMPUTER:
1380      for my $computer (@sectordb) {
1381         my ($mac_address, $attribute) = %{$computer};
1382         next LOOP_ON_COMPUTER if $mac_address ne $mac;
1383
1384         if ($attribute->{'ip'} ne $pool) {
1385            die "Error: host enable $mac [FAILED]" .
1386               " ... The host $mac does not belong to the $pool pool.\n";
1387            }
1388
1389         if ($attribute->{'enabled'} eq 'yes') {
1390            print "Info: host $mac from pool $pool is already enable [OK]" .
1391               " ... Status: $attribute->{'enabled'}\n";
1392            exit;
1393            }
1394
1395         my $timestamp = time;
1396         $attribute->{'modify_time'} = $timestamp;
1397         $attribute->{'enabled'}     = 'yes';
1398         ipamdb_save("$COMPUTER_YAML", $computer_db);
1399         print "Info: enabling host $mac from pool $pool [OK]" .
1400            " ... Status: $attribute->{'enabled'}\n";
1401         exit;
1402         }
1403      }
1404   }
1405
1406#-------------------------------------------------------------------------------
1407
1408sub cmd_enable_pc {
1409   local @ARGV = @_;
1410
1411   my $help = get_cmd_name();
1412   my ($hostname, $sector, $ip);
1413
1414   GetOptions(
1415      'hostname|h=s'    => \$hostname,
1416      'sector|s|d=s'    => \$sector,
1417      'ip|i=s'          => \$ip,
1418      );
1419
1420   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1421   exit_on_error_option($help)
1422      if $sector  eq '';
1423   exit_on_error_option($help)
1424      if $hostname   eq ''
1425      and $ip        eq '';
1426   exit_on_error_option($help)
1427      if $hostname   ne ''
1428      and $ip        ne '';
1429
1430   enable_pc($hostname, $sector, $ip);
1431   }
1432
1433#-------------------------------------------------------------------------------
1434
1435sub cmd_disable_pc {
1436   local @ARGV = @_;
1437
1438   my $help = get_cmd_name();
1439   my ($hostname, $sector, $ip);
1440
1441   GetOptions(
1442      'hostname|h=s'    => \$hostname,
1443      'sector|s|d=s'    => \$sector,
1444      'ip|i=s'          => \$ip,
1445      );
1446
1447   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1448   exit_on_error_option($help)
1449      if $sector  eq '';
1450   exit_on_error_option($help)
1451      if $hostname   eq ''
1452      and $ip        eq '';
1453   exit_on_error_option($help)
1454      if $hostname   ne ''
1455      and $ip        ne '';
1456
1457   disable_pc($hostname, $sector, $ip);
1458   }
1459
1460#-------------------------------------------------------------------------------
1461
1462sub cmd_disable_float {
1463   local @ARGV = @_;
1464
1465   my $help = get_cmd_name();
1466   my ($pool, $mac);
1467
1468   GetOptions(
1469      'pool|p=s'  => \$pool,
1470      'mac|m=s'   => \$mac,
1471      );
1472
1473   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
1474   exit_on_error_option($help)
1475      if $pool eq ''
1476      or $mac  eq '';
1477
1478   disable_float($pool, $mac);
1479   }
1480
1481#-------------------------------------------------------------------------------
1482
1483sub cmd_enable_float {
1484   local @ARGV = @_;
1485
1486   my $help = get_cmd_name();
1487   my ($pool, $mac);
1488
1489   GetOptions(
1490      'pool|p=s'  => \$pool,
1491      'mac|m=s'   => \$mac,
1492      );
1493
1494   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
1495   exit_on_error_option($help)
1496      if $pool eq ''
1497      or $mac  eq '';
1498
1499   enable_float($pool, $mac);
1500   }
1501
1502#-------------------------------------------------------------------------------
1503# DELETE section
1504#-------------------------------------------------------------------------------
1505
1506#-------------------------------------------------------------------------------
1507#Nom: del_pc
1508#Description: supprime une machine en DHCP ou en IP statique.
1509
1510sub del_pc {
1511   my ($hostname, $sector, $ip) = @_;
1512
1513   my $computer_db = ipamdb_load($COMPUTER_YAML);
1514
1515   control_exist_sector($computer_db, $sector) or exit;
1516   if ($ip ne '') { # delete by IP
1517      if ( control_exist_ip($computer_db, $ip) == 1 ) {
1518         die "Error: unkown IP address: $ip\n";
1519         }
1520
1521      my $computer_index = 0;
1522
1523      LOOP_ON_COMPUTER:
1524      for my $computer (@{$computer_db->{$sector}}) {
1525         my ($mac_address, $attribute) = %{$computer};
1526
1527         $computer_index++, next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
1528         
1529         splice(@{$computer_db->{$sector}}, $computer_index => 1);
1530         ipamdb_save("$COMPUTER_YAML", $computer_db);
1531         print "Info: host $ip has been removed from the sector $sector [OK]\n";
1532         exit;
1533         }
1534      }
1535   else {
1536      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
1537         die "Error: unkown host: $hostname, in sector: $sector\n";
1538         }
1539
1540      my $computer_index = 0;
1541
1542      LOOP_ON_COMPUTER:
1543      for my $computer (@{$computer_db->{$sector}}) {
1544         my ($mac_address, $attribute) = %{$computer};
1545
1546         $computer_index++, next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
1547
1548         if ($attribute->{'address_type'} eq 'pool-dhcp') {
1549            die "Error: host remove $hostname from the sector $sector [FAILED]" .
1550               " ... The host $hostname belongs to a DHCP pool.\n";
1551            }
1552
1553         splice(@{$computer_db->{$sector}}, $computer_index => 1);
1554         ipamdb_save("$COMPUTER_YAML", $computer_db);
1555         print "Info: host $hostname has been removed from the sector $sector [OK]\n";
1556         exit;
1557         }
1558      }
1559   }
1560
1561#-------------------------------------------------------------------------------
1562#Nom: del_float
1563#Description: supprime une machine d'un pool DHCP
1564
1565sub del_float {
1566   my ($pool, $mac) = @_;
1567
1568   my $computer_db = ipamdb_load($COMPUTER_YAML);
1569
1570   if ( control_exist_mac($computer_db, $mac) == 1 ) {
1571      print "Adresse MAC $mac non trouvée.\n";
1572      exit;
1573      }
1574
1575   for my $sector_current (keys %{$computer_db}) {
1576      next if $sector_current eq 'dset';
1577      next if $sector_current eq 'pool';
1578      next if $sector_current eq 'pxe';
1579      next if $sector_current eq 'tag';
1580      next if $sector_current eq 'version';
1581
1582      my @sectordb = @{$computer_db->{$sector_current}};
1583
1584      my $computer_index = 0;
1585
1586      LOOP_ON_COMPUTER:
1587      for my $computer (@sectordb) {
1588         my ($mac_address, $attribute) = %{$computer};
1589
1590         $computer_index++, next LOOP_ON_COMPUTER if $mac_address ne $mac;
1591
1592         if ($attribute->{'ip'} ne $pool) {
1593            die "Error: host remove $mac [FAILED]" .
1594               " ... The host $mac does not belong to the $pool pool.\n";
1595            }
1596
1597         splice(@{$computer_db->{$sector_current}}, $computer_index => 1);
1598         ipamdb_save("$COMPUTER_YAML", $computer_db);
1599         print "Info: remove host $mac from the pool $pool [OK]\n";
1600         exit;
1601         }
1602      }
1603   }
1604
1605#-------------------------------------------------------------------------------
1606
1607sub cmd_del_pc {
1608   local @ARGV = @_;
1609
1610   my $help = get_cmd_name();
1611   my ($hostname, $sector, $ip);
1612
1613   GetOptions(
1614      'hostname|h=s'    => \$hostname,
1615      'sector|s|d=s'    => \$sector,
1616      'ip|i=s'          => \$ip,
1617      );
1618
1619   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1620   exit_on_error_option($help)
1621      if $sector  eq '';
1622   exit_on_error_option($help)
1623      if $hostname   eq ''
1624      and $ip        eq '';
1625   exit_on_error_option($help)
1626      if $hostname   ne ''
1627      and $ip        ne '';
1628
1629   del_pc($hostname, $sector, $ip);
1630   }
1631
1632#-------------------------------------------------------------------------------
1633
1634sub cmd_del_float {
1635   local @ARGV = @_;
1636
1637   my $help = get_cmd_name();
1638   my ($pool, $mac);
1639
1640   GetOptions(
1641      'pool|p=s'        => \$pool,
1642      'mac|m=s'         => \$mac,
1643      );
1644
1645   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
1646   exit_on_error_option($help)
1647      if $pool eq ''
1648      or $mac  eq '';
1649
1650   del_float($pool, $mac);
1651   }
1652
1653#-------------------------------------------------------------------------------
1654# SECTOR section
1655#-------------------------------------------------------------------------------
1656
1657sub cmd_create_sector {
1658   local @ARGV = @_;
1659
1660   my $help = get_cmd_name();
1661   my ($sector, $dns_extension, $comment);
1662
1663   GetOptions(
1664      'sector|s|d=s'       => \$sector,
1665      'dns-extension|e=s'  => \$dns_extension,
1666      'comment|c=s'        => \$comment,
1667      );
1668
1669   exit_on_error_option($help)
1670      if $sector        eq ''
1671      or $dns_extension eq ''
1672      or $comment       eq '';
1673
1674   $sector =~ m{^[\w\d]}            or die "Error: sector $sector must begin by a letter or a digit\n";
1675   $sector =~ m{^[\w\d][\w\d-_]*$}  or die "Error: sector $sector must only use letter digit dash and underscore character\n";
1676
1677   $comment = normalize_comment($comment);
1678
1679   my $computer_db = ipamdb_load($COMPUTER_YAML);
1680
1681   $computer_db->{'dset'} ||= {};
1682   die "Error: sector already exists: $sector\n" if exists $computer_db->{'dset'}{$sector};
1683
1684   control_syntax_comment($comment)    or exit;
1685
1686   my $timestamp = time;
1687   $computer_db->{'dset'}{$sector} = {
1688      'dns_extension'   => $dns_extension,
1689      'comment'         => $comment,
1690      'create_time'     => $timestamp,
1691      'modify_time'     => $timestamp,
1692      };
1693   $computer_db->{$sector} ||= []; # Create empty sector computer list by default
1694   ipamdb_save("$COMPUTER_YAML", $computer_db);
1695   }
1696
1697#-------------------------------------------------------------------------------
1698
1699sub cmd_sector_add_ip {
1700   local @ARGV = @_;
1701
1702   my $help = get_cmd_name();
1703   my ($sector, $ip_range);
1704
1705   GetOptions(
1706      'sector|s|d=s' => \$sector,
1707      'ip-range|i=s' => \$ip_range,
1708      );
1709
1710   exit_on_error_option($help)
1711      if $sector     eq ''
1712      or $ip_range   eq '';
1713
1714   control_syntax_cidr($ip_range)   or die "Error: bad IP range $ip_range syntax (CIDR)\n";;
1715
1716   my $computer_db = ipamdb_load($COMPUTER_YAML);
1717   exists $computer_db->{'dset'}{$sector} or die "Error: sector not exists: $sector\n";
1718
1719   my $timestamp = time;
1720   $computer_db->{'dset'}{$sector}{'ip_range'} ||= [];
1721   LOOP_ON_CIDR:
1722   for my $cidr_current (@{$computer_db->{'dset'}{$sector}{'ip_range'}}) {
1723      next LOOP_ON_CIDR if $cidr_current ne $ip_range;
1724     
1725      die "Error: IP range $ip_range already in sector $sector\n";
1726      }
1727
1728   my $timestamp = time;
1729   push @{$computer_db->{'dset'}{$sector}{'ip_range'}}, $ip_range;
1730   $computer_db->{'dset'}{$sector}{'modify_time'} = $timestamp;
1731   ipamdb_save("$COMPUTER_YAML", $computer_db);
1732   }
1733
1734#-------------------------------------------------------------------------------
1735# POOL section
1736#-------------------------------------------------------------------------------
1737
1738#-------------------------------------------------------------------------------
1739#Nom: create_pool
1740#Description: crée un pool dans le fichier de données YAML et dans le DHCP.
1741#
1742#Commentaires: il y a un petit bug si jamais on rentre que des adresses ip qui existent déjà.
1743#              Le pool est créé mais sans adresses ip.
1744
1745sub cmd_create_pool {
1746   local @ARGV = @_;
1747
1748   my $help = get_cmd_name();
1749   my ($pool, $sector, $file_pool, $ipaddress_pool);
1750
1751   GetOptions(
1752      'pool|p=s'           => \$pool,
1753      'sector|s|d=s'      => \$sector,
1754      'file-pool|f=s'      => \$file_pool,
1755      'ipaddress-pool|i=s' => \$ipaddress_pool,
1756      );
1757
1758   exit_on_error_option($help)
1759      if $pool             eq ''
1760      or $sector        eq ''
1761      or $file_pool        eq ''
1762      or $ipaddress_pool   eq '';
1763
1764   my $computer_db = ipamdb_load($COMPUTER_YAML);
1765
1766   if ($computer_db->{'pool'}) {
1767      die "Error: pool already exists: $pool\n" if exists $computer_db->{'pool'}{$pool};
1768      }
1769
1770   #--- control if the domain's pool exist ---#
1771   control_exist_sector($computer_db, $sector) or exit;
1772
1773   my @ip_list = ();
1774   #---control if address exist ---#
1775   if ($ipaddress_pool =~ /,/) {
1776      LOOP_ON_IP:
1777      for my $ip (split /,/, $ipaddress_pool) {
1778         if ($ip =~ /-/) {
1779            my ($ip1, $ip2, $ip3, $range) = split /\./, $ip;
1780            my ($first, $last) = split /-/, $range;
1781            for (my $cpt = $first; $cpt <= $last; $cpt++) {
1782               my $ip_loc = "$ip1.$ip2.$ip3.$cpt";
1783               control_syntax_ip($ip_loc) or die "Error: bad IP syntax: $ip_loc\n";
1784               control_exist_ip($computer_db, $ip_loc) or die "Error: IP address already exists: $ip_loc\n";
1785               push @ip_list, $ip_loc;
1786               }
1787            }
1788         else {
1789            control_syntax_ip($ip) or next LOOP_ON_IP;
1790            if ( control_exist_ip($computer_db, $ip) == 0 ) {
1791               print "L'adresse IP $ip existe déjà\n";
1792               next;
1793               }
1794            push @ip_list, $ip;
1795            }
1796         }
1797      }
1798
1799   my $timestamp = time;
1800   $computer_db->{'pool'}{$pool} = {
1801      'ip'          => [@ip_list],
1802      'enabled'     => 'yes',
1803      'create_time' => $timestamp,
1804      'modify_time' => $timestamp,
1805      'file'        => $file_pool,
1806      'domain'      => $sector,
1807      };
1808   ipamdb_save("$COMPUTER_YAML", $computer_db);
1809   }
1810
1811#-------------------------------------------------------------------------------
1812
1813sub cmd_show_pool {
1814   local @ARGV = @_;
1815
1816   my ($no_header);
1817
1818   GetOptions(
1819      'no-header|H' => \$no_header,
1820      );
1821
1822   my $computer_db = ipamdb_load($COMPUTER_YAML);
1823
1824   printf "%-17s %-17s %s\n", 'Pool', 'File', 'DNS-Domain' if not $no_header;
1825   LOOP_ON_PXE:
1826   for my $pool ( keys %{$computer_db->{'pool'}} ) {
1827
1828      printf "%-17s %-17s %s\n",
1829         $pool,
1830         $computer_db->{'pool'}{$pool}{'file'},
1831         $computer_db->{'pool'}{$pool}{'domain'},
1832      }
1833   }
1834
1835#-------------------------------------------------------------------------------
1836# PXE section
1837#-------------------------------------------------------------------------------
1838
1839sub cmd_create_pxe {
1840   local @ARGV = @_;
1841
1842   my $help = get_cmd_name();
1843   my ($pxe_config, $ip_next_server, $filename, $comment);
1844
1845   GetOptions(
1846      'bootp|b=s'       => \$pxe_config,
1847      'next-server|n=s' => \$ip_next_server,
1848      'filename|f=s'    => \$filename,
1849      'comment|c=s'     => \$comment,
1850      );
1851
1852   exit_on_error_option($help)
1853      if $pxe_config       eq ''
1854      or $ip_next_server   eq ''
1855      or $filename         eq ''
1856      or $comment          eq '';
1857
1858   my $computer_db = ipamdb_load($COMPUTER_YAML);
1859
1860   $comment = normalize_comment($comment);
1861
1862   $computer_db->{'pxe'} ||= {};
1863   die "Error: PXE config already exists: $pxe_config\n" if exists $computer_db->{'pxe'}{$pxe_config};
1864
1865   control_syntax_ip($ip_next_server)  or die "Error: bad IP syntax: $ip_next_server\n";
1866   control_syntax_comment($comment)    or exit;
1867
1868   my $timestamp = time;
1869   $computer_db->{'pxe'}{$pxe_config} = {
1870      'ip_next_server'  => $ip_next_server,
1871      'filename'        => $filename,
1872      'comment'         => $comment,
1873      'create_time'     => $timestamp,
1874      'modify_time'     => $timestamp,
1875      };
1876   ipamdb_save("$COMPUTER_YAML", $computer_db);
1877   }
1878
1879#-------------------------------------------------------------------------------
1880
1881sub cmd_remove_pxe {
1882   local @ARGV = @_;
1883
1884   my $help = get_cmd_name();
1885   my ($pxe_config);
1886
1887   GetOptions(
1888      'bootp|b=s' => \$pxe_config,
1889      );
1890
1891   exit_on_error_option($help)
1892      if $pxe_config eq '';
1893
1894   my $computer_db = ipamdb_load($COMPUTER_YAML);
1895
1896   $computer_db->{'pxe'} ||= {};
1897   die "Error: PXE config does not exist: $pxe_config\n" if not exists $computer_db->{'pxe'}{$pxe_config};
1898
1899   # Test if some computer use this config
1900   LOOP_ON_SECTOR:
1901   for my $sector_current (keys %{$computer_db}) {
1902      next if $sector_current eq 'dset';
1903      next if $sector_current eq 'pool';
1904      next if $sector_current eq 'pxe';
1905      next if $sector_current eq 'tag';
1906      next if $sector_current eq 'version';
1907
1908      LOOP_ON_COMPUTER:
1909      for my $computer (@{$computer_db->{$sector_current}}) {
1910         my ($mac_address, $attribute) = %{$computer};
1911
1912         if (exists $attribute->{'pxe_config'}) {
1913            my $hostname = $attribute->{'hostname'};
1914            die "Error: computer still use this PXE config: $hostname.$sector_current $mac_address\n" if $pxe_config eq $attribute->{'pxe_config'};
1915            }
1916         }
1917      }
1918
1919   delete $computer_db->{'pxe'}{$pxe_config};
1920   ipamdb_save("$COMPUTER_YAML", $computer_db);
1921   }
1922
1923#--------------------------------------------------------------------------------
1924
1925sub cmd_show_pxe {
1926   local @ARGV = @_;
1927
1928   my ($no_header);
1929
1930   GetOptions(
1931      'no-header|H' => \$no_header,
1932      );
1933
1934   my $computer_db = ipamdb_load($COMPUTER_YAML);
1935
1936   printf "%-12s %-13s %-30s %s\n", 'PXE-Config', 'Next-Server', 'Filename', 'Comment' if not $no_header;
1937   LOOP_ON_PXE:
1938   for my $pxe_config ( keys %{$computer_db->{'pxe'}} ) {
1939      my $ip_next_server = $computer_db->{'pxe'}{$pxe_config}{'ip_next_server'};
1940      my $filename       = $computer_db->{'pxe'}{$pxe_config}{'filename'};
1941      my $comment        = $computer_db->{'pxe'}{$pxe_config}{'comment'};
1942
1943      printf "%-12s %-13s %-30s %s\n", $pxe_config, $ip_next_server, $filename, $comment;
1944      }
1945   }
1946
1947#-------------------------------------------------------------------------------
1948
1949sub cmd_enable_pxe {
1950   local @ARGV = @_;
1951
1952   my $help = get_cmd_name();
1953   my ($hostname, $sector, $ip, $pxe_config);
1954
1955   GetOptions(
1956      'hostname|h=s'    => \$hostname,
1957      'sector|s|d=s'    => \$sector,
1958      'ip|i=s'          => \$ip,
1959      'bootp|b=s'       => \$pxe_config,
1960      );
1961
1962   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1963   exit_on_error_option($help)
1964      if $sector  eq ''
1965      or $pxe_config eq '';
1966   exit_on_error_option($help)
1967      if $hostname   eq ''
1968      and $ip        eq '';
1969   exit_on_error_option($help)
1970      if $hostname   ne ''
1971      and $ip        ne '';
1972
1973   my $computer_db = ipamdb_load($COMPUTER_YAML);
1974
1975   die "Error: PXE config not exists: $pxe_config\n" if not exists $computer_db->{'pxe'}{$pxe_config};
1976
1977   control_exist_sector($computer_db, $sector) or exit;
1978   if ($ip ne '') {
1979      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
1980      if ( control_exist_ip($computer_db, $ip) == 1 ) {
1981         die "Error: unkown IP address: $ip\n";
1982         }
1983
1984      for my $sector_current (keys %{$computer_db}) {
1985         next if $sector_current eq 'dset';
1986         next if $sector_current eq 'pool';
1987         next if $sector_current eq 'pxe';
1988         next if $sector_current eq 'tag';
1989         next if $sector_current eq 'version';
1990
1991         LOOP_ON_COMPUTER:
1992         for my $computer (@{$computer_db->{$sector_current}}) {
1993            my ($mac_address, $attribute) = %{$computer};
1994            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
1995
1996            $attribute->{'modify_time'} = time;
1997            $attribute->{'pxe_config'}  = $pxe_config;
1998            ipamdb_save("$COMPUTER_YAML", $computer_db);
1999            print "Info: host $attribute->{'hostname'} ($sector_current), IP $ip, PXE enabled: $pxe_config\n";
2000            exit;
2001            }
2002         }
2003      }
2004   else {
2005      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
2006         die "Error: unkown host: $hostname, in sector: $sector\n";
2007         }
2008
2009      LOOP_ON_COMPUTER:
2010      for my $computer (@{$computer_db->{$sector}}) {
2011         my ($mac_address, $attribute) = %{$computer};
2012         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
2013         
2014         if ($attribute->{'address_type'} eq 'pool-dhcp') {
2015            die "Error. Host $hostname ($sector) in a pool. No PXE possible [FAILED]\n";
2016            }
2017
2018         $attribute->{'modify_time'} = time;
2019         $attribute->{'pxe_config'}  = $pxe_config;
2020         ipamdb_save("$COMPUTER_YAML", $computer_db);
2021         print "Info: host $hostname ($sector), IP $attribute->{'ip'}, PXE enabled: $pxe_config [OK]\n";
2022         exit;
2023         }
2024      }
2025   }
2026
2027#-------------------------------------------------------------------------------
2028
2029sub cmd_disable_pxe {
2030   local @ARGV = @_;
2031
2032   my $help = get_cmd_name();
2033   my ($hostname, $sector, $ip);
2034
2035   GetOptions(
2036      'hostname|h=s'    => \$hostname,
2037      'sector|s|d=s'    => \$sector,
2038      'ip|i=s'          => \$ip,
2039      );
2040
2041   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
2042   exit_on_error_option($help)
2043      if $sector  eq '';
2044   exit_on_error_option($help)
2045      if $hostname   eq ''
2046      and $ip        eq '';
2047   exit_on_error_option($help)
2048      if $hostname   ne ''
2049      and $ip        ne '';
2050
2051   my $computer_db = ipamdb_load($COMPUTER_YAML);
2052
2053   control_exist_sector($computer_db, $sector) or exit;
2054   if ($ip ne '') {
2055      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
2056      if ( control_exist_ip($computer_db, $ip) == 1 ) {
2057         die "Error: unkown IP address: $ip\n";
2058         }
2059
2060      for my $sector_current (keys %{$computer_db}) {
2061         next if $sector_current eq 'dset';
2062         next if $sector_current eq 'pool';
2063         next if $sector_current eq 'pxe';
2064         next if $sector_current eq 'tag';
2065         next if $sector_current eq 'version';
2066
2067         LOOP_ON_COMPUTER:
2068         for my $computer (@{$computer_db->{$sector_current}}) {
2069            my ($mac_address, $attribute) = %{$computer};
2070           
2071            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
2072            next LOOP_ON_COMPUTER if not exists $attribute->{'pxe_config'};
2073
2074            my $pxe_config = $attribute->{'pxe_config'};
2075            $attribute->{'modify_time'} = time;
2076            delete $attribute->{'pxe_config'};
2077            ipamdb_save("$COMPUTER_YAML", $computer_db);
2078            print "Info: IP address: $ip, PXE disable from config: $pxe_config [OK]\n";
2079            exit;
2080            }
2081         }
2082      }
2083   else {
2084      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
2085         die "Error: unkown host: $hostname, in sector: $sector\n";
2086         }
2087
2088      LOOP_ON_COMPUTER:
2089      for my $computer (@{$computer_db->{$sector}}) {
2090         my ($mac_address, $attribute) = %{$computer};
2091
2092         next LOOP_ON_COMPUTER if $attribute->{'hostname'} eq $hostname;
2093
2094         if ($attribute->{'address_type'} eq 'pool-dhcp') {
2095            die "Error: host $hostname ($sector) in a pool. No PXE possible [FAILED]\n";
2096            }
2097
2098         next LOOP_ON_COMPUTER if not exists $attribute->{'pxe_config'};
2099
2100         my $pxe_config = $attribute->{'pxe_config'};
2101         $attribute->{'modify_time'} = time;
2102         delete $attribute->{'pxe_config'};
2103         ipamdb_save("$COMPUTER_YAML", $computer_db);
2104         print "Info: host $hostname ($sector), PXE disable from config: $pxe_config [OK]\n";
2105         exit;
2106         }
2107      }
2108   }
2109
2110#-------------------------------------------------------------------------------
2111# TAG section
2112#-------------------------------------------------------------------------------
2113
2114sub cmd_create_tag {
2115   local @ARGV = @_;
2116
2117   my $help = get_cmd_name();
2118   my ($tag, $comment);
2119
2120   GetOptions(
2121      'tag|t=s'      => \$tag,
2122      'comment|c=s'  => \$comment,
2123      );
2124
2125   exit_on_error_option($help)
2126      if $tag     eq ''
2127      or $comment eq '';
2128
2129   my $computer_db = ipamdb_load($COMPUTER_YAML);
2130
2131   $comment = normalize_comment($comment);
2132
2133   $computer_db->{'tag'} ||= {};
2134   die "Error: TAG already exists: $tag\n" if exists $computer_db->{'tag'}{$tag};
2135
2136   die "Error: TAG 'universal' is intrinsic. It's not possible to create it.\n" if $tag eq 'universal';
2137
2138   if ($tag !~ m/^ \w+ $/xms) {
2139      die "Error: bad format for TAG (alphanumeric string): $tag\n";
2140      }
2141
2142   control_syntax_comment($comment) or exit;
2143
2144   my $timestamp = time;
2145   $computer_db->{'tag'}{$tag} = {
2146      'comment'         => $comment,
2147      'create_time'     => $timestamp,
2148      'modify_time'     => $timestamp,
2149      };
2150   ipamdb_save("$COMPUTER_YAML", $computer_db);
2151   }
2152
2153#-------------------------------------------------------------------------------
2154
2155sub cmd_remove_tag {
2156   local @ARGV = @_;
2157
2158   my $help = get_cmd_name();
2159   my ($tag);
2160
2161   GetOptions(
2162      'tag|t=s' => \$tag,
2163      );
2164
2165   exit_on_error_option($help)
2166      if $tag eq '';
2167
2168   my $computer_db = ipamdb_load($COMPUTER_YAML);
2169
2170   $computer_db->{'tag'} ||= {};
2171   die "Error: TAG does not exist: $tag\n" if not exists $computer_db->{'tag'}{$tag};
2172
2173   # Test if some computer use this config
2174   LOOP_ON_SECTOR:
2175   for my $sector_current (keys %{$computer_db}) {
2176      next if $sector_current eq 'dset';
2177      next if $sector_current eq 'pool';
2178      next if $sector_current eq 'pxe';
2179      next if $sector_current eq 'tag';
2180      next if $sector_current eq 'version';
2181
2182      LOOP_ON_COMPUTER:
2183      for my $computer (@{$computer_db->{$sector_current}}) {
2184         my ($mac_address, $attribute) = %{$computer};
2185
2186         if (exists $attribute->{'tag'}) {
2187            my $hostname = $attribute->{'hostname'};
2188            die "Error: computer still use this TAG: $hostname.$sector_current $mac_address\n" if $tag eq $attribute->{'tag'};
2189            }
2190         }
2191      }
2192
2193   delete $computer_db->{'tag'}{$tag};
2194   ipamdb_save("$COMPUTER_YAML", $computer_db);
2195   }
2196
2197#--------------------------------------------------------------------------------
2198
2199sub cmd_show_tag {
2200   local @ARGV = @_;
2201
2202   my ($no_header);
2203
2204   GetOptions(
2205      'no-header|H' => \$no_header,
2206      );
2207
2208   my $computer_db = ipamdb_load($COMPUTER_YAML);
2209
2210   printf "%-12s %s\n", 'TAG', 'Comment' if not $no_header;
2211   LOOP_ON_TAG:
2212   for my $tag ( keys %{$computer_db->{'tag'}} ) {
2213      my $comment = $computer_db->{'tag'}{$tag}{'comment'};
2214
2215      printf "%-12s %s\n", $tag, $comment;
2216      }
2217   }
2218
2219#--------------------------------------------------------------------------------
2220# GLOBAL section
2221#--------------------------------------------------------------------------------
2222
2223sub cmd_upgrade_db {
2224   my $flag_change;
2225
2226   my $computer_db = ipamdb_load($COMPUTER_YAML);
2227
2228   LOOP_ON_SECTOR:
2229   for my $sector_current (keys %{$computer_db}) {
2230      next if $sector_current eq 'dset';
2231      next if $sector_current eq 'pool';
2232      next if $sector_current eq 'pxe';
2233      next if $sector_current eq 'tag';
2234      next if $sector_current eq 'version';
2235
2236      my @sectordb = @{$computer_db->{$sector_current}};
2237
2238      LOOP_ON_COMPUTER:
2239      for my $computer (@sectordb) {
2240         my ($mac_address, $attribute) = %{$computer};
2241         my $new_mac = normalize_mac_address($mac_address);
2242         print "perl -pi -e 's/$mac_address:/$new_mac:/' $COMPUTER_YAML\n" if "$mac_address" ne "$new_mac";
2243
2244         my $comment = $attribute->{'comment'};
2245         $comment =~ s/\s\s+/ /g and $flag_change++;
2246         $comment =~ s/^\s+\S//  and $flag_change++;
2247         $comment =~ s/\S\s+$//  and $flag_change++;
2248         $comment =~ s{^(\d\d\d\d)\/O(\d\/\d\d)}{$1/0$2} and $flag_change++;
2249         $comment =~ s{^(\d\d\d\d\/\d\d\/)O(\d)}{$1/0$2} and $flag_change++;
2250         $comment =~ s{^(\d\d\d\d)\/(\d\d)\/(\d\d)}{$1-$2-$3} and $flag_change++;
2251         if ($comment !~ m/^\d\d\d\d-\d\d-\d\d/) {
2252            print "# no date at beginning of comment $mac_address\n";
2253            }
2254
2255         $attribute->{'comment'} = $comment;
2256         }
2257      }
2258   print "# FLAG :$flag_change\n";
2259
2260   ipamdb_save("$COMPUTER_YAML", $computer_db) if $flag_change;
2261   }
2262
2263#--------------------------------------------------------------------------------
2264
2265sub cmd_show_sector {
2266   local @ARGV = @_;
2267
2268   my ($no_header);
2269
2270   GetOptions(
2271      'no-header|H' => \$no_header,
2272      );
2273
2274   my $computer_db = ipamdb_load($COMPUTER_YAML);
2275
2276   my $tb_computer = Text::Table->new(
2277     {align  => 'left',   align_title => 'left',   title => 'Sector'},
2278     {is_sep => 1,        body        => '  '},
2279     {align  => 'left',   align_title => 'left',   title => 'DNS-Extension'},
2280     {is_sep => 1,        body        => '  '},
2281     {align  => 'left',   align_title => 'left',   title => 'IP-Range'},
2282     {align  => 'left',   align_title => 'left',   title => 'Date'},
2283     {align  => 'left',   align_title => 'left',   title => 'Comment'},
2284     {align  => 'left',   align_title => 'left',   title => 'Category'},
2285     );
2286
2287   LOOP_ON_SECTOR:
2288   for my $sector_current (sort keys %{$computer_db}) {
2289      next if $sector_current eq 'dset';
2290      next if $sector_current eq 'pool';
2291      next if $sector_current eq 'pxe';
2292      next if $sector_current eq 'tag';
2293      next if $sector_current eq 'version';
2294
2295      $tb_computer->add($sector_current), next LOOP_ON_SECTOR if not exists $computer_db->{'dset'}{$sector_current};
2296
2297      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computer_db->{'dset'}{$sector_current}{'modify_time'};
2298      $year += 1900;
2299      $mon++;
2300      my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;
2301
2302      my $ip_range;
2303      $ip_range = join ',', @{$computer_db->{'dset'}{$sector_current}{'ip_range'}} if exists $computer_db->{'dset'}{$sector_current}{'ip_range'};
2304
2305      my $category;
2306      my $comment = $computer_db->{'dset'}{$sector_current}{'comment'};
2307      $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
2308      $comment =~ s/\s+(\(\w+\))$// and $category = $1;
2309
2310      $tb_computer->add($sector_current,
2311         $computer_db->{'dset'}{$sector_current}{'dns_extension'},
2312         $ip_range,
2313         $date,
2314         $comment,
2315         $category,
2316         );
2317      }
2318
2319   print $tb_computer->title(),
2320         $tb_computer->rule('-') if not $no_header;
2321   print $tb_computer->body();
2322   }
2323
2324#--------------------------------------------------------------------------------
2325
2326sub cmd_search_mac {
2327   local @ARGV = @_;
2328
2329   my $help = get_cmd_name();
2330   my ($mac);
2331
2332   GetOptions(
2333      'mac|m=s' => \$mac,
2334      );
2335
2336   exit_on_error_option($help)
2337      if $mac eq '';
2338
2339   $mac = normalize_mac_address($mac);
2340
2341   my $computer_db = ipamdb_load($COMPUTER_YAML);
2342
2343   control_syntax_mac_address($mac) or exit;
2344
2345   LOOP_ON_SECTOR:
2346   for my $sector_current (keys %{$computer_db}) {
2347      next if $sector_current eq 'dset';
2348      next if $sector_current eq 'pool';
2349      next if $sector_current eq 'pxe';
2350      next if $sector_current eq 'tag';
2351      next if $sector_current eq 'version';
2352
2353      my @sectordb = @{$computer_db->{$sector_current}};
2354
2355      LOOP_ON_COMPUTER:
2356      for my $computer (@sectordb) {
2357         my ($mac_address, $attribute) = %{$computer};
2358
2359         next LOOP_ON_COMPUTER if $mac_address ne $mac;
2360
2361         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $attribute->{'modify_time'};
2362         $year += 1900;
2363         $mon++;
2364         my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;
2365
2366         my $comment = $attribute->{'comment'};
2367         $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
2368
2369         my $enable = $attribute->{'enabled'};
2370         if (exists $attribute->{'pxe_config'}) {
2371            $enable .= '/' . $attribute->{'pxe_config'};
2372            }
2373         if (exists $attribute->{'tag'}) {
2374            $enable .= ':' . $attribute->{'tag'};
2375            }
2376
2377         printf "%-30s  %-20s %17s %9s %3s %10s %s\n",
2378            $attribute->{'hostname'} . '.' . $sector_current,
2379            $attribute->{'ip'},
2380            $mac_address,
2381            $attribute->{'address_type'},
2382            $enable,
2383            $date,
2384            $comment;
2385         }
2386      }
2387   }
2388
2389#--------------------------------------------------------------------------------
2390#Nom: show
2391#Description: liste les machines à partir du fichier YAML par nom de domaine.
2392
2393sub cmd_show_host {
2394   my %ipdb = ();
2395
2396   my $computer_db = ipamdb_load($COMPUTER_YAML);
2397
2398   my $tb_computer = Text::Table->new(
2399     {align  => 'left',   align_title => 'left',   title => 'Hostname.Sector'},
2400     {is_sep => 1,        body        => '  '},
2401     {align  => 'left',   align_title => 'left',   title => 'IPv4-Address'},
2402     {is_sep => 1,        body        => '  '},
2403     {align  => 'center', align_title => 'center', title => 'MAC-Address'},
2404     {is_sep => 1,        body        => '  '},
2405     {align  => 'right',  align_title => 'right',  title => 'Type'},
2406     {align  => 'right',  align_title => 'right',  title => 'Status'},
2407     {is_sep => 1,        body        => '  '},
2408     {align  => 'left',   align_title => 'left',   title => 'Date'},
2409     {align  => 'left',   align_title => 'left',   title => 'Comment'},
2410     );
2411
2412  LOOP_ON_SECTOR:
2413   for my $sector_current (sort keys %{$computer_db}) {
2414      next if $sector_current eq 'dset';
2415      next if $sector_current eq 'pool';
2416      next if $sector_current eq 'pxe';
2417      next if $sector_current eq 'tag';
2418      next if $sector_current eq 'version';
2419
2420      my @sectordb = @{$computer_db->{$sector_current}};
2421
2422      LOOP_ON_COMPUTER:
2423      for my $computer (@sectordb) {
2424         my ($mac_address, $attribute) = %{$computer};
2425         my $ip = $attribute->{'ip'};
2426
2427         if ($ip =~ m/$DDT::RE::IPv4_ADDRESS/xms) {
2428            if ( not exists $ipdb{$ip} ) {
2429               $ipdb{$ip} = {
2430                  'mac_address'  => $mac_address,
2431                  %{$attribute},
2432                  'sector'    => $sector_current,
2433                  };
2434               }
2435            else {
2436               print {*STDERR} "# Warning: $ip already exists in the database with MAC $mac_address!\n";
2437               }
2438            next LOOP_ON_COMPUTER;
2439            }
2440
2441         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $attribute->{'modify_time'};
2442         $year += 1900;
2443         $mon++;
2444         my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;
2445
2446         my $comment = normalize_comment($attribute->{'comment'});
2447         $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
2448
2449         my $enable = $attribute->{'enabled'};
2450         if (exists $attribute->{'pxe_config'}) {
2451            $enable .= '/' . $attribute->{'pxe_config'};
2452            }
2453         if (exists $attribute->{'tag'}) {
2454            $enable .= ':' . $attribute->{'tag'};
2455            }
2456
2457         #printf "%-30s  %-20s %17s %9s %3s %10s %s\n",
2458         $tb_computer->add(
2459            $attribute->{'hostname'} . '.' . $sector_current,
2460            $ip,
2461            $mac_address,
2462            $attribute->{'address_type'},
2463            $enable,
2464            $date,
2465            $comment,
2466            );
2467         }
2468      #print "\n# *** List of pool computers in the sector: $sector_current ***\n";
2469      }
2470
2471   #print "\n# *** List of computers ordered by IP and sector ***\n";
2472   LOOP_ON_IP_ADDRESS:
2473   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %ipdb)) {
2474      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $ipdb{$ip}->{'modify_time'};
2475      $year += 1900;
2476      $mon++;
2477      my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;
2478
2479      my $comment =$ipdb{$ip}->{'comment'};
2480      $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
2481
2482      my $enable = $ipdb{$ip}->{'enabled'};
2483      if (exists $ipdb{$ip}->{'pxe_config'}) {
2484         $enable .= '/' . $ipdb{$ip}->{'pxe_config'};
2485         }
2486      if (exists $ipdb{$ip}->{'tag'}) {
2487         $enable .= ':' . $ipdb{$ip}->{'tag'};
2488         }
2489
2490      #printf "%-30s %-20s %17s %9s %3s %10s %s\n",
2491      $tb_computer->add(
2492         $ipdb{$ip}->{'hostname'} . '.' . $ipdb{$ip}->{'sector'},
2493         $ip,
2494         normalize_mac_address($ipdb{$ip}->{'mac_address'}),
2495         $ipdb{$ip}->{'address_type'},
2496         $enable,
2497         $date,
2498         $comment
2499         );
2500      }
2501
2502   print $tb_computer->title();
2503   print $tb_computer->rule('-');
2504   print $tb_computer->body();
2505   }
2506
2507#-------------------------------------------------------------------------------
2508#Nom: cmd_generate_dhcp_file
2509#Description: génère les fichiers de configuration des machines et des pools du dhcp
2510
2511sub cmd_generate_dhcp_file {
2512   backup_database();
2513
2514   my $computer_db = ipamdb_load($COMPUTER_YAML);
2515
2516   my %file_pool;
2517
2518   for my $sector_current (keys %{$computer_db}) {
2519      next if $sector_current eq 'dset';
2520      next if $sector_current eq 'pool';
2521      next if $sector_current eq 'pxe';
2522      next if $sector_current eq 'tag';
2523      next if $sector_current eq 'version';
2524
2525      open FILE_VLAN, '>', "$FOLDER_GEN_DHCP/$sector_current";
2526      my @sectordb = @{$computer_db->{$sector_current}};
2527      for my $value (@sectordb) {
2528         ALL_MAC_ADDRESS:
2529         for my $mac_addres (keys %{$value}) {
2530            #host pcdavoust {  deny-unknown-clients;
2531            #hardware ethernet 0:6:5b:b8:13:d1;
2532            #fixed-address 194.254.66.72;
2533            #}
2534
2535            my $hostname     = $value->{$mac_addres}{'hostname'};
2536            my $ip           = $value->{$mac_addres}{'ip'};
2537            my $comment      = $value->{$mac_addres}{'comment'};
2538            my $address_type = $value->{$mac_addres}{'address_type'};
2539            my $enabled      = $value->{$mac_addres}{'enabled'};
2540            my $tags         = $value->{$mac_addres}{'tag'} || 'universal';
2541
2542            my $buffer;
2543            if ($address_type eq 'dhcp') {
2544               if ($enabled eq 'yes') {
2545                  $buffer  = "host $hostname {\n"; # deny-unknown-clients;
2546                  $buffer .= "   hardware ethernet $mac_addres;\n";
2547                  $buffer .= "   fixed-address $ip;\n";
2548
2549                  if (exists $value->{$mac_addres}{'pxe_config'}) {
2550                     my $pxe_config     = $value->{$mac_addres}{'pxe_config'};
2551                     my $ip_next_server = $computer_db->{'pxe'}{$pxe_config}{'ip_next_server'};
2552                     my $filename       = $computer_db->{'pxe'}{$pxe_config}{'filename'};
2553                     $buffer .= "   next-server $ip_next_server;\n";
2554                     $buffer .= "   filename \"$filename\";\n";
2555                     }
2556                  $buffer .= "   #comment: $comment\n";
2557                  $buffer .= "   }\n";
2558                  $buffer .= "\n";
2559
2560                  for my $tag (split/,/, $tags) {
2561                     $file_pool{"tag-$tag"} ||= [];
2562                     push @{$file_pool{"tag-$tag"}}, "subclass \"tag-$tag\" 1:$mac_addres; # $comment\n";
2563                     }
2564                  }
2565               else {
2566                  $buffer  = "#host $hostname {\n"; # deny-unknown-clients;
2567                  $buffer .= "#   hardware ethernet $mac_addres;\n";
2568                  $buffer .= "#   fixed-address $ip;\n";
2569                  $buffer .= "#   comment: $comment \n";
2570                  $buffer .= "#   }\n";
2571                  $buffer .= "\n";
2572                  }
2573               print FILE_VLAN $buffer;
2574               }
2575            elsif ($address_type eq 'pool-dhcp') {
2576               #--- Génère les fichiers pool dhcp ---#
2577               for my $current_pool (keys %{$computer_db->{'pool'}}) {
2578                  next if $current_pool ne $ip;
2579
2580                  if ($enabled eq 'yes') {
2581                     $buffer = "subclass \"$current_pool\" 1:$mac_addres; # $comment\n";
2582
2583                     for my $tag (split/,/, $tags) {
2584                        $file_pool{"tag-$tag"} ||= [];
2585                        push @{$file_pool{"tag-$tag"}}, "subclass \"tag-$tag\" 1:$mac_addres; # $comment\n";
2586                        }
2587                     }
2588                  else {
2589                     $buffer = "#subclass \"$current_pool\" 1:$mac_addres; # $comment\n";
2590                     }
2591
2592                  my $current_pool_file_name = $computer_db->{'pool'}{$current_pool}{'file'};
2593
2594                  $file_pool{$current_pool_file_name} ||= [];
2595                  push @{$file_pool{$current_pool_file_name}}, $buffer;
2596                  }
2597               }
2598            }
2599         }
2600
2601      close FILE_VLAN;
2602
2603      for my $file_name (keys %file_pool) {
2604         open FILE_POOL, '>', "$FOLDER_GEN_DHCP/$file_name";
2605         print FILE_POOL @{$file_pool{$file_name}};
2606         close FILE_POOL;
2607         }
2608      }
2609      print "Copy DHCP files from $FOLDER_GEN_DHCP to /etc/dhcp/include/\n";
2610      exec $SCRIPT_UPDATE;
2611   }
2612
2613#-------------------------------------------------------------------------------
2614#Nom: cmd_generate_dns_file
2615#Description: génère les fichiers d'enregistrements DNS
2616
2617sub cmd_generate_dns_file {
2618   local @ARGV = @_;
2619
2620   my $help = get_cmd_name();
2621   my ($verbose);
2622
2623   GetOptions(
2624      'verbose|v' => \$verbose,
2625      );
2626
2627   my $buffer_fwd;
2628   my $buffer_rev;
2629   my $pool_domain;
2630
2631   my $computer_db = ipamdb_load($COMPUTER_YAML);
2632
2633   for my $sector_current (keys %{$computer_db}) {
2634      next if $sector_current eq 'dset';
2635      next if $sector_current eq 'pool';
2636      next if $sector_current eq 'pxe';
2637      next if $sector_current eq 'tag';
2638      next if $sector_current eq 'version';
2639
2640      if ($sector_current eq 'pool') {
2641         LOOP_ON_COMPUTER:
2642         for my $computer (@{$computer_db->{$sector_current}}) {
2643            for my $pool_name (keys %{$computer}) {
2644               $pool_domain = $computer->{$pool_name}->{'domain'}."\n";
2645               #print $computer->{$pool_name}->{'file'};
2646               chomp $pool_domain;
2647               open FILE_FORWARD_DNS, '>>', "$FOLDER_GEN_DNS/db.$pool_domain.fwd";
2648               open FILE_REVERSE_DNS, '>>', "$FOLDER_GEN_DNS/db.$pool_domain.rev";
2649               my @T_pool_ip = @{$computer->{$pool_name}->{'ip'}};
2650               for my $pool_ip (@T_pool_ip) {
2651                  my @T_split = split(/\./ , $pool_ip);
2652                  $buffer_fwd = sprintf "%-24s IN  A  %-15s ;\n", "$pool_name$T_split[3]", $pool_ip;
2653                  $buffer_rev = "$T_split[3]   IN PTR   $pool_name$T_split[3].$pool_domain.\n";
2654                  print FILE_FORWARD_DNS $buffer_fwd;
2655                  print FILE_REVERSE_DNS $buffer_rev;
2656                  }
2657               close FILE_FORWARD_DNS;
2658               close FILE_REVERSE_DNS;
2659               }
2660            }
2661         }
2662
2663      else {
2664         #--- Création du fichier non-reverse ---#
2665         open FILE_FORWARD_DNS, ">> $FOLDER_GEN_DNS/db.$sector_current.fwd";
2666         open FILE_REVERSE_DNS, ">> $FOLDER_GEN_DNS/db.$sector_current.rev";
2667
2668         my @sectordb = @{$computer_db->{$sector_current}};
2669
2670         LOOP_ON_COMPUTER:
2671         for my $computer (@sectordb) {
2672            my ($mac_address, $attribute) = %{$computer};
2673
2674            #host pcdavoust {  deny-unknown-clients;
2675            #hardware ethernet 0:6:5b:b8:13:d1;
2676            #fixed-address 194.254.66.72;
2677            #}
2678
2679            my $hostname     = $attribute->{'hostname'};
2680            my $ip           = $attribute->{'ip'};
2681            my $comment      = $attribute->{'comment'};
2682            my $address_type = $attribute->{'address_type'};
2683            my $enabled      = $attribute->{'enabled'};
2684
2685            next LOOP_ON_COMPUTER if not (($address_type eq 'dhcp') or ($address_type eq 'static'));
2686
2687            my $dns_domain = $sector_current;
2688            if (exists $computer_db->{'dset'}{$sector_current}) {
2689               $dns_domain = $computer_db->{'dset'}{$sector_current}{'dns_extension'};
2690               }
2691
2692            my @ip_split = split /\./, $ip;
2693            if ($enabled eq 'yes') {
2694               if (exists $attribute->{'dns_extension'}
2695                     and "$attribute->{'dns_extension'}" != "$dns_domain") {
2696                  print "A FAIRE\n";
2697                  }
2698               $buffer_fwd = sprintf "%-24s  IN A   %-15s ; %s\n", $hostname, $ip, $comment;
2699               $buffer_rev = sprintf "%3i    IN PTR %-15s\n", $ip_split[3], "$hostname.$dns_domain.";
2700               }
2701
2702            else {
2703               $buffer_fwd = sprintf ";%-24s IN A   %-15s ; %s\n", $hostname, $ip, $comment;
2704               $buffer_rev = sprintf ";%3i   IN PTR %-15s\n", $ip_split[3], "$hostname.$dns_domain.";
2705               }
2706            print FILE_REVERSE_DNS $buffer_rev;
2707            print FILE_FORWARD_DNS $buffer_fwd;
2708            }
2709         close FILE_REVERSE_DNS;
2710         close FILE_FORWARD_DNS;
2711         print "- DNS: db.$sector_current.fwd db.$sector_current.rev [CREATE].\n" if $verbose;
2712         print "  Ex : sort -k 4n -t . $FOLDER_GEN_DNS/db.$sector_current.fwd\n"     if $verbose;
2713         }
2714      }
2715   }
2716
2717#--------------------------------------------------------------------------------
2718
2719sub shell_command {
2720   my $cmd = shift;
2721
2722   require FileHandle;
2723   my $fh     = new FileHandle;
2724   my @result = ();
2725   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
2726   @result = <$fh>;
2727   close $fh;
2728   chomp @result;
2729   return @result;
2730   }
2731
2732#--------------------------------------------------------------------------------
2733
2734sub cmd_check_dns {
2735   local @ARGV = @_;
2736
2737   my $help = get_cmd_name();
2738   my ($opt_direct, $opt_reverse, $opt_byip);
2739
2740   GetOptions(
2741      'direct|d'  => \$opt_direct,
2742      'reverse|r' => \$opt_reverse,
2743      'by-ip|b'   => \$opt_byip,
2744      );
2745
2746   my $computer_db = ipamdb_load($COMPUTER_YAML);
2747
2748   if ($opt_direct or not ($opt_reverse or $opt_byip)) { # DDT to DNS check
2749      LOOP_ON_SECTOR:
2750      for my $sector_current (keys %{$computer_db}) {
2751         next if $sector_current eq 'dset';
2752         next if $sector_current eq 'pool';
2753         next if $sector_current eq 'pxe';
2754         next if $sector_current eq 'tag';
2755         next if $sector_current eq 'version';
2756
2757         my @sectordb = @{$computer_db->{$sector_current}};
2758
2759         LOOP_ON_COMPUTER:
2760         for my $computer (@sectordb) {
2761            my ($mac_address, $attribute) = %{$computer};
2762            #my $new_mac = normalize_mac_address($mac_address);
2763            my $ip = $attribute->{'ip'};
2764            next LOOP_ON_COMPUTER if not $ip =~ m/$DDT::RE::IPv4_ADDRESS/xms;
2765            next LOOP_ON_COMPUTER if $attribute->{'enabled'} eq 'no';
2766
2767            my $dns_hostname_fq = scalar gethostbyaddr(inet_aton($ip), AF_INET);
2768            my ($dns_hostname) = split /\./, $dns_hostname_fq;
2769
2770            if ($attribute->{'hostname'} ne $dns_hostname) {
2771               print "$mac_address ($sector_current) $ip - $dns_hostname / $attribute->{'hostname'} # $attribute->{'comment'}\n";
2772               next LOOP_ON_COMPUTER;
2773               }
2774
2775            my $packed_ip = scalar gethostbyname($dns_hostname_fq);
2776            if (defined $packed_ip) {
2777               my $ip_address = inet_ntoa($packed_ip);
2778               if ($ip ne $ip_address) {
2779                  print "Error: bad IP $ip for reverse DNS on $dns_hostname_fq\n";
2780                  next LOOP_ON_COMPUTER;
2781                  }
2782               }
2783            }
2784         }
2785      }
2786
2787   if ($opt_reverse) {  # DNS to DDT check
2788      my %saw; # count for unique member
2789      my @dns_domain_list = sort grep !$saw{$_}++,
2790         map $computer_db->{'dset'}{$_}{'dns_extension'},
2791         grep exists($computer_db->{'dset'}{$_}{'dns_extension'}),
2792         keys $computer_db->{'dset'};
2793      LOOP_ON_DNS:
2794      for my $dns (@dns_domain_list) {
2795         LOOP_ON_IP:
2796         for (shell_command("host -t A -l $dns")) {
2797            # smtp2.legi.grenoble-inp.fr has address 194.254.67.37
2798            next if not m/has address/;
2799            next if not m/^(\w[\w-_\.]+\w)\s+has\saddress\s+(\d[\d\.]+\d)$/;
2800            my ($hostname_fq, $ip) = ($1, $2);
2801            control_syntax_ip($ip) or next LOOP_ON_IP;
2802            if (control_exist_ip($computer_db, $ip) == 1) {
2803               printf "Unkown IP: %-15s / %s\n", $ip, $hostname_fq;
2804               next LOOP_ON_IP;
2805               }
2806            }
2807         }
2808      }
2809
2810   if ($opt_byip) {  # IP Range DDT check
2811      my @ip_check;
2812      LOOP_ON_SECTOR:
2813      for my $sector_current (keys %{$computer_db->{'dset'}}) {
2814         next LOOP_ON_SECTOR if not exists $computer_db->{'dset'}{$sector_current}{'ip_range'};
2815
2816         LOOP_ON_CIDR:
2817         for my $ip_range (@{$computer_db->{'dset'}{$sector_current}{'ip_range'}}) {
2818
2819            LOOP_ON_IP:
2820            for my $ip (NetAddr::IP->new($ip_range)->hostenum()) {
2821               $ip =~ s{/32$}{};
2822               
2823               my $dns_hostname_fq = scalar gethostbyaddr(inet_aton($ip), AF_INET);
2824               my ($dns_hostname) = split /\./, $dns_hostname_fq;
2825
2826               # Verify reverse return same IP
2827               my $packed_ip = scalar gethostbyname($dns_hostname_fq);
2828               if (defined $packed_ip) {
2829                  my $ip_address = inet_ntoa($packed_ip);
2830                  if ($ip ne $ip_address) {
2831                     print "Error: bad IP $ip for reverse DNS on $dns_hostname_fq sector $sector_current\n";
2832                     }
2833                  }
2834
2835               # Verify direct return same hostname
2836               LOOP_ON_COMPUTER:
2837               for my $computer (@{$computer_db->{$sector_current}}) {
2838                  my ($mac_address, $attribute) = %{$computer};
2839
2840                  next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
2841
2842                  print "Error: bad DNS host $dns_hostname / DDT host $attribute->{'hostname'} in sector $sector_current with IP $ip\n"
2843                     if $attribute->{'hostname'} ne $dns_hostname and $attribute->{'enabled'} ne 'no';
2844
2845                  next LOOP_ON_IP;
2846                  }
2847
2848               # Declare in DNS but not in DDT
2849               print "Error: DNS host $dns_hostname in sector $sector_current with IP $ip not declare in DDT database\n" if $dns_hostname ne '';
2850               }
2851            }
2852         }
2853      }
2854   }
2855
2856#-------------------------------------------------------------------------------
2857#Nom: load_data_dhcp
2858#Description: permet de charger le fichier de données YAML via les fichiers de configuration
2859#             machines.
2860#            ATTENTION: LES COMMENTAIRES DU FICHIER DISPARAITRONT.
2861
2862sub load_data_dhcp {
2863   my ($sector, $input_file) = @_;
2864
2865   my $computer_db = ipamdb_load($COMPUTER_YAML);
2866
2867   my @T_mac;
2868   my @T_host;
2869   my @T_ip;
2870   my $cpt;
2871   open (FILE, "<$input_file");
2872   my @buffer = <FILE>;
2873   close(FILE);
2874
2875   LINE:
2876   for my $ligne (@buffer) {
2877      #--
2878      $ligne =~ s/#.*$//;
2879      $ligne =~ s/\s+/ /;
2880      $ligne =~ s/^\s+//;
2881      next if $ligne eq '';
2882
2883      if ($ligne =~ /^host /) {
2884         $cpt=0;
2885         my @T_split = split(/host\s+/, $ligne);
2886         @T_host = split(/ /, $T_split[1]);
2887         chomp($T_host[0]);
2888
2889         $cpt++;
2890         }
2891
2892      if ($ligne =~ /^*ethernet /) {
2893         $ligne =~ s/;//g;
2894         @T_mac = split(/ethernet\s+/, $ligne);
2895         chomp($T_mac[1]);
2896         $cpt++;
2897         }
2898
2899      if ($ligne =~ /^*address /) {
2900         $ligne =~ s/;//g;
2901         @T_ip = split(/address\s+/, $ligne);
2902         chomp($T_ip[1]);
2903
2904         $cpt++;
2905         }
2906
2907      if ($cpt == 3) {
2908         #   print "MAC $T_mac[1] HOST $T_host[0] IP $T_ip[1].\n";
2909         my $mac = $T_mac[1];
2910         my $hostname = $T_host[0];
2911         my $ip = $T_ip[1];
2912         $cpt = 0;
2913
2914         if ( control_exist_hostname($computer_db, $sector, $hostname) == 0 ) {
2915            print "Error: host already exist in sector $sector: $hostname\n";
2916            next LINE;
2917            }
2918         control_syntax_mac_address($mac) or next LINE;
2919         if ( control_exist_mac($computer_db, $mac) == 0) {
2920            print "Error: physical MAC address already exists: $mac\n";
2921            next LINE;
2922            }
2923
2924         control_syntax_ip($ip) or next LINE;
2925         if ( control_exist_ip($computer_db, $ip) == 0 ) {
2926            print "Error: IP address already exists: $ip\n";
2927            next LINE;
2928            }
2929         my $timestamp = time;
2930         push @{$computer_db->{$sector}}, { $mac => {
2931            'hostname'     => $hostname,
2932            'ip'           => $ip,
2933            'address_type' => 'dhcp',
2934            'enabled'      => 'yes',
2935            'create_time'  => $timestamp,
2936            'modify_time'  => $timestamp,
2937            'alias'        =>  '',
2938            }};
2939         }
2940      }
2941   }
2942
2943#-------------------------------------------------------------------------------
2944#Nom: load_data_pool
2945#Description: permet de charger le fichier YAML via les fichiers de conf 'pool' du dhcp.
2946
2947sub load_data_pool {
2948   my ($sector, $input_file) = @_;
2949
2950   my @T_mac;
2951
2952   open (FILE, "<$input_file");
2953   my @buffer = <FILE>;
2954   close(FILE);
2955
2956   my $computer_db = ipamdb_load($COMPUTER_YAML);
2957
2958   for my $ligne (@buffer) {
2959      #--
2960      $ligne =~ s/#.*$//;
2961      $ligne =~ s/\s+/ /;
2962      $ligne =~ s/^\s+//;
2963      $ligne =~ s/;//g;
2964      $ligne =~ s/"//g;
2965      next if $ligne eq '';
2966
2967      if (($ligne =~ /^subclass/)) {
2968         my @T_split = split(/ / ,$ligne);
2969         my $pool = $T_split[1];
2970
2971         @T_mac = split(/:/ , $T_split[2]);
2972         my $mac = $T_mac[1].":".$T_mac[2].":".$T_mac[3].":".$T_mac[4].":".$T_mac[5].":".$T_mac[6];
2973         control_syntax_mac_address($mac) or next;
2974         if (control_exist_mac($computer_db, $mac) == 0) {
2975            print "Error: physical MAC address already exists: $mac\n";
2976            next;
2977            }
2978
2979         #--- cette partie teste si le pool existe.
2980         if (not exists $computer_db->{'pool'}{$pool}) {
2981            print "Error: create pool with create_pool command before load database: $pool\n";
2982            exit;
2983            }
2984
2985         if ($computer_db->{'pool'}{'domain'} eq $sector) {
2986            my $timestamp = time;
2987            push @{$computer_db->{$sector}}, { $mac => {
2988               'hostname'     => $pool,
2989               'ip'           => $pool,
2990               'address_type' => 'pool-dhcp',
2991               'enabled'      => 'yes',
2992               'create_time'  => $timestamp,
2993               'modify_time'  => $timestamp,
2994               }};
2995            }
2996         else {
2997            print "Ajout de la machine $mac [FAILED]\n";
2998            print "Error: the pool doesn't exists: $pool, for the domain: $sector\n";
2999            }
3000         }
3001      }
3002   }
3003
3004#-------------------------------------------------------------------------------
3005
3006sub load_data_file {
3007   my ($sector, $input_file, $type_file) = @_;
3008
3009   my $computer_db = ipamdb_load($COMPUTER_YAML);
3010
3011   #$computer_db
3012   if ($type_file eq 'dhcp') {
3013      load_data_dhcp($sector, $input_file);
3014      }
3015
3016   elsif ($type_file eq 'pool-dhcp') {
3017      load_data_pool($sector, $input_file);
3018      }
3019
3020   ipamdb_save("$COMPUTER_YAML", $computer_db);
3021   }
3022
3023#-------------------------------------------------------------------------------
3024
3025sub cmd_load_database {
3026   local @ARGV = @_;
3027
3028   my $help = get_cmd_name();
3029   my ($sector, $input_file, $type_file);
3030
3031   GetOptions(
3032      'sector|s|d=s'    => \$sector,
3033      'filename|f=s'    => \$input_file,
3034      'kind|k=s'        => \$type_file,
3035      );
3036
3037   exit_on_error_option($help)
3038      if $sector  eq ''
3039      or $input_file eq ''
3040      or $type_file  eq '';
3041
3042   load_data_file($sector, $input_file, $type_file);
3043   }
3044
3045#-------------------------------------------------------------------------------
3046#Nom: backup_database
3047#Description: sauvegarde et réinitialise les fichiers d'enregistrements DHCP.
3048
3049sub backup_database {
3050   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime time;
3051   $year += 1900;
3052   $mon++;
3053   my $date = sprintf '%04i-%02i-%02i-%02i-%02i-%02i', $year, $mon, $mday, $hour, $min, $sec;
3054
3055   copy($COMPUTER_YAML, "$FOLDER_BACKUP/$COMPUTER_BASENAME-$date.conf") or die "Error: database copy backup failed: $!\n";
3056   }
3057
3058#-------------------------------------------------------------------------------
3059# HELP section
3060#-------------------------------------------------------------------------------
3061
3062#-------------------------------------------------------------------------------
3063#Nom: exit_on_error_option
3064#Description: messages d'aide des options pour les différentes commandes
3065
3066sub exit_on_error_option {
3067  my ($command) = @_;
3068
3069   if ($command eq 'add-dhcp') {
3070      print "List of options for command: $command\n";
3071      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3072      print " -h : computer hostname (mandatory if option -i != 'pool'). Example: -h info8pc154\n";
3073      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A\n";
3074      print " -i : internet IP address (mandatory). Possible value: classical IP address or the keyword 'pool'\n";
3075      print " -p : name of the DHCP pool to which the machine belongs (mandatory if option -i == 'pool')\n";
3076      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
3077      print "Example:\n";
3078      print " ddt add-dhcp -h most1mc130 -s legi-sector03 -i 194.254.66.130 -m 00:17:F2:D3:2B:FF -c '2008-07-03 Mac Book Guillaume Balleyrac (MOST)\n";
3079      }
3080
3081   elsif ($command eq 'add-float') {
3082      print "List of options for command: $command\n";
3083      print " -s : sector attachment (mandatory)\n";
3084      print " -p : name of the DHCP pool to which the machine belongs (mandatory)\n";
3085      print " -m : physical MAC address (mandatory)\n";
3086      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
3087      print "Example:\n";
3088      print " ddt add-float -p pool-stagiaire -s legi-pool -i 192.168.10.1 -m 00:AB:1B:CC:AA:2F -c '2013-09-25 Dell OptiPlex 745 - Eric Goncalves (NRJ)\n";
3089      }
3090
3091   elsif ($command eq 'add-static') {
3092      print "List of options for command: $command\n";
3093      print " -s : sector attachment (mandatory)\n";
3094      print " -i : internet IP address (mandatory)\n";
3095      print " -h : computer hostname (mandatory)\n";
3096      print " -m : physical MAC address (mandatory)\n";
3097      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
3098      print "Example:\n";
3099      print " ddt add-static -h legipc1 -s legi-sector03 -i 192.168.10.1 -m 00:AB:1B:CC:AA:2F -c '2013-09-25 Dell OptiPlex 745 - Eric Goncalves (NRJ)\n";
3100      }
3101
3102   elsif ($command eq 'add-virtual') {
3103      print "List of options for command: $command\n";
3104      print " -s : sector attachment (mandatory)\n";
3105      print " -i : internet IP address (mandatory)\n";
3106      print " -h : computer hostname (mandatory)\n";
3107      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
3108      print "Example:\n";
3109      print " ddt add-virtual -h legipc1 -s legi-sector03 -i 192.168.10.1 -c '2013-09-25 Dell OptiPlex 745 - Eric Goncalves (NRJ)\n";
3110      }
3111
3112   elsif ($command eq 'add-alias') {
3113      print "List of options for command: $command\n";
3114      print " -s : sector attachment (mandatory)\n";
3115      print " -h : computer hostname (mandatory)\n";
3116      print " -a : computer alias name (mandatory)\n";
3117      }
3118
3119   elsif ($command eq 'create-sector') {
3120      print "List of options for command: $command\n";
3121      print " -s : new sector (mandatory)\n";
3122      print " -e : DNS domain name extension( mandatory). Example legi.grenoble-inp.fr\n";
3123      print " -c : comment (mandatory). Example: 2016-08-22 VLAN legi-261 (INFO)\n";
3124      print "Examples:\n";
3125      print " ddt create-sector -s legi-sector03 -e legi.grenoble-inp.fr -c '2016-08-22 VLAN legi-261 (INFO)'\n";
3126      }
3127
3128   elsif ($command eq 'create-pool') {
3129      print "List of options for command: $command\n";
3130      print " -p : name of the DHCP pool. Example: pool-legi-priv\n";
3131      print " -s : sector attachment for the pool. (sector attachment must exist in file $COMPUTER_BASENAME.conf). Example: legi-sector03\n";
3132      print " -f : configuration filename on the DHCP server for the pool\n";
3133      print " -i : adresse(s) IP ou plage d'IP. Séparateur d'adresses IP: ','. Séparateur de plage '-'\n";
3134      print "Examples:\n";
3135      print " ddt create-pool -p legi-pool1 -s legi-sector03 -f legi-pool-private -i 192.168.10.1,192.168.10.2,192.168.10.3\n";
3136      print " ddt create-pool -p legi-pool2 -s legi-sector03 -f legi-pool-public  -i 192.168.10.1-192.168.10.4\n";
3137      }
3138
3139   elsif ($command eq 'create-pxe') {
3140      print "List of options for command: $command\n";
3141      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
3142      print " -n : internet IP address for the DHCP next-server.\n";
3143      print " -f : filename on TFTP server to load at boot\n";
3144      print " -c : comment (mandatory). Example: 2014-04-07 PXE Boot for CentOS (MOST)\n";
3145      }
3146
3147   elsif ($command eq 'remove-pxe') {
3148      print "List of options for command: $command\n";
3149      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
3150      }
3151
3152   elsif ($command eq 'enable-pxe') {
3153      print "List of options for command: $command\n";
3154      print " -h : computer hostname (mandatory unless option -i)\n";
3155      print " -i : internet IP address (mandatory unless option -h)\n";
3156      print " -s : sector attachment (mandatory if option -h)\n";
3157      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
3158      }
3159
3160   elsif ($command eq 'disable-pxe') {
3161      print "List of options for command: $command\n";
3162      print " -h : computer hostname (mandatory unless option -i)\n";
3163      print " -i : internet IP address (mandatory unless option -h)\n";
3164      print " -s : sector attachment (mandatory if option -h)\n";
3165      }
3166
3167   elsif ($command eq 'create-tag') {
3168      print "List of options for command: $command\n";
3169      print " -t : name of the TAG (mandatory). Example: restricted\n";
3170      print " -c : comment (mandatory). Example: 2014-04-07 tag restricted (INFO)\n";
3171      print "tag 'universal' is intrinsic\n";
3172      }
3173
3174   elsif ($command eq 'remove-tag') {
3175      print "List of options for command: $command\n";
3176      print " -b : name of the TAG. Example: restricted\n";
3177      }
3178
3179   elsif ($command eq 'change-mac') {
3180      print "List of options for command: $command\n";
3181      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3182      print " -h : computer hostname (mandatory unless option -i)\n";
3183      print " -i : internet IP address (mandatory unless option -h). Possible value: classical IP address or the keyword 'pool'\n";
3184      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
3185      }
3186
3187   elsif ($command eq 'change-ip') {
3188      print "List of options for command: $command\n";
3189      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3190      print " -h : computer hostname (mandatory)\n";
3191      print " -i : new internet IP address (mandatory). Possible value: classical IP address\n";
3192      }
3193
3194   elsif ($command eq 'change-host') {
3195      print "List of options for command: $command\n";
3196      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3197      print " -i : internet IP address (mandatory). Possible value: classical IP address\n";
3198      print " -h : new computer hostname (mandatory)\n";
3199      print "It's not possible to change hostname for computer that belongs to a pool\n";
3200      }
3201
3202   elsif ($command eq 'change-comment') {
3203      print "List of options for command: $command\n";
3204      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3205      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
3206      print " -c : new comment (mandatory)\n";
3207      }
3208
3209   elsif ($command eq 'change-sector') {
3210      print "List of options for command: $command\n";
3211      print " -s : new sector attachment (mandatory). Example: -s legi-sector03\n";
3212      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
3213      print " -i : internet IP address (mandatory)\n";
3214      }
3215
3216   elsif ($command eq 'change-tag') {
3217      print "List of options for command: $command\n";
3218      print " -h : computer hostname (mandatory unless option -i or -m)\n";
3219      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3220      print " -i : internet IP address (mandatory unless option -h or -m)\n";
3221      print " -m : physical MAC address (mandatory unless option -h or -i, priority). Example: -m 0F:58:AB:2A:22:11\n";
3222      print " -t : list of tags separated by comma (mandatory). Example: -t internal,windows\n";
3223      }
3224
3225   elsif ($command eq 'change-type') {
3226      print "List of options for command: $command\n";
3227      print " -h : computer hostname (mandatory)\n";
3228      print " -s : sector attachment (mandatory)\n";
3229      print " -t : new type). Example: -t static\n";
3230      }
3231
3232   elsif ($command eq 'load-database') {
3233      print "List of options for command: $command\n";
3234      print " -s : sector attachment\n";
3235      print " -f : input file in DHCP format\n";
3236      print " -k : possible cases (kind): dhcp, pool-dhcp, fix-address\n";
3237      }
3238
3239   elsif ($command eq 'enable-pc') {
3240      print "List of options for command: $command\n";
3241      print " -h : computer hostname (mandatory unless option -i)\n";
3242      print " -i : internet IP address (mandatory unless option -h)\n";
3243      print " -s : sector attachment (mandatory if option -h)\n";
3244      print "Examples:\n";
3245      print " ddt enable-pc -i 192.168.10.1\n";
3246      print " ddt enable-pc -s legi-sector03 -h kevinpc\n";
3247      }
3248
3249   elsif ($command eq 'enable-float') {
3250      print "List of options for command: $command\n";
3251      print " -m : physical MAC address (mandatory)\n";
3252      print " -p : name of the DHCP pool (mandatory)\n";
3253      }
3254
3255   elsif ($command eq 'disable-float') {
3256      print "List of options for command: $command\n";
3257      print " -m : physical MAC address (mandatory)\n";
3258      print " -p : name of the DHCP pool (mandatory)\n";
3259      }
3260
3261   elsif ($command eq 'disable-pc') {
3262      print "List of options for command: $command\n";
3263      print " -h : computer hostname (mandatory unless option -i)\n";
3264      print " -i : internet IP address (mandatory unless option -h)\n";
3265      print " -s : sector attachment (mandatory if option -h)\n";
3266      print "Examples:\n";
3267      print " ddt disable-pc -i 192.168.10.1\n";
3268      print " ddt disable-pc -s legi-sector03 -h kevinpc\n";
3269      }
3270
3271   elsif ($command eq 'del-pc') {
3272      print "List of options for command: $command\n";
3273      print " -s : sector attachment (mandatory)\n";
3274      print " -h : computer hostname (mandatory unless option -i)\n";
3275      print " -i : internet IP address (mandatory unless option -h)\n";
3276      }
3277
3278   elsif ($command eq 'del-float') {
3279      print "List of options for command: $command\n";
3280      print " -m : physical MAC address (mandatory)l\n";
3281      print " -p : name of the DHCP pool\n";
3282      }
3283
3284   elsif ($command eq 'search-mac') {
3285      print "List of options for command: $command\n";
3286      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
3287      }
3288
3289   elsif ($command eq 'sector-add-ip') {
3290      print "List of options for command: $command\n";
3291      print " -s : sector attachment (mandatory)\n";
3292      print " -i : internet IP range address in CIDR notation (mandatory)\n";
3293      }
3294
3295   else {
3296      print "No help for command: $command\n";
3297      }
3298   exit;
3299   }
3300
3301#-------------------------------------------------------------------------------
3302
3303sub cmd_version {
3304
3305   print <<'END';
3306ddt - management of computer names and IP addresses
3307Copyright (C) 2006-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
3308Main author Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
3309License GNU GPL version 2 or later and Perl equivalent
3310END
3311
3312   print "Database Version 1\n";
3313   print "Version $VERSION\n\n";
3314   print ' $Id: ddt 365 2018-09-17 09:24:54Z g7moreau $'."\n";
3315   return;
3316   }
3317
3318#-------------------------------------------------------------------------------
3319#Nom: usage
3320#Description: message d'aide sur les commandes du script
3321
3322sub cmd_help {
3323   print <<END;
3324ddt - management of computer names and IP addresses
3325
3326 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]
3327 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3328 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3329 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3330 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]
3331 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3332 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3333 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3334 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3335 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3336 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]
3337 ddt change-type [--hostname|-h hostname] [--sector|-s|-d sector] [--type|-t type]
3338 ddt check-dns [--direct] [--reverse]
3339 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]
3340 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]
3341 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]
3342 ddt create-tag [--tag|-t tag] [--comment|-c comment]
3343 ddt del-float [--pool|-p pool] [--mac|-m mac]
3344 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3345 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3346 ddt disable-float [--pool|-p pool] [--mac|-m mac]
3347 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3348 ddt enable-float [--pool|-p pool] [--mac|-m mac]
3349 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3350 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]
3351 ddt gen-dhcp-file
3352 ddt gen-dns-file [--verbose]
3353 ddt help
3354 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]
3355 ddt remove-pxe [--bootp|-b pxe_config]
3356 ddt remove-tag [--tag|-t tag]
3357 ddt search-mac [--mac|-m mac]
3358 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr]
3359 ddt show-sector [--no-header|-H]
3360 ddt show
3361 ddt show-pool [--no-header|-H]
3362 ddt show-pxe [--no-header|-H]
3363 ddt show-tag [--no-header|-H]
3364 ddt version
3365
3366COMMANDS
3367
3368 * add-alias         : add an alias for a computer (like CNAME for the DNS)
3369 * add-dhcp          : add a computer with a fix DHCP IP or in a DHCP pool
3370 * add-float         : add a computer with an IP in a DHCP pool
3371 * add-static        : add a computer with a static IP
3372 * add-virtual       : add a virtual computer with a static IP but a virtual MAC (useful to declare float computer in DNS)
3373 * change-comment    : change the computer comment
3374 * change-sector     : change the sector attachment for a computer
3375 * change-host       : change the computer hostname
3376 * change-ip         : change the computer IP address
3377 * change-mac        : change the computer physical MAC address
3378 * change-tag        : change the list of TAGs associated to a computer
3379 * change-type       : change the type associated to a computer
3380 * check-dns         : check the DNS table for base IPs
3381 * create-sector     : create a new sector
3382 * create-pool       : create a new pool for DHCP records
3383 * create-pxe        : create a new PXE/BOOTP configuration
3384 * create-tag        : create a new TAG
3385 * del-float         : remove a computer from a DHCP pool
3386 * del-pc            : remove a computer (DHCP or static IP) from the YAML database
3387 * disable-pc        : disable a computer (DHCP and/or DNS) (but keep it in the database)
3388 * disable-float     : disable a computer from a DHCP pool (but keep it in the database)
3389 * disable-pxe       : remove PXE/BOOTP configuration on a computer
3390 * enable-float      : enable a previous disable computer (DHCP and/or DNS)
3391 * enable-pc         : enable a previous disable computer (DHCP and/or DNS)
3392 * enable-pxe        : enable PXE/BOOTP configuration on a computer
3393 * gen-dhcp-file     : generate DHCP files for the isc DHCP server
3394 * gen-dns-file      : generate DNS files for the bind domain server
3395 * help              : this help
3396 * load-database     : load the YAML database (be careful)
3397 * remove-pxe        : remove a PXE/BOOTP configuration
3398 * remove-tag        : remove a TAG
3399 * search-mac        : search physical MAC address computer
3400 * ddt sector-add-ip : add IP range check on a sector
3401 * show-sector       : list all sector group of computer
3402 * show              : list all computers
3403 * show-pool         : list all pool
3404 * show-pxe          : list PXE/BOOTP configuration
3405 * show-tag          : list all TAGs
3406 * version           : return program version
3407END
3408   return;
3409   }
3410
3411################################################################
3412# documentation
3413################################################################
3414
3415__END__
3416
3417=head1 NAME
3418
3419ddt - management of computer names and IP addresses
3420
3421
3422=head1 USAGE
3423
3424 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]
3425 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3426 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3427 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3428 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]
3429 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3430 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3431 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3432 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3433 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3434 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]
3435 ddt change-type [--hostname|-h hostname] [--sector|-s|-d sector] [--type|-t type]
3436 ddt check-dns [--direct] [--reverse]
3437 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]
3438 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]
3439 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]
3440 ddt create-tag [--tag|-t tag] [--comment|-c comment]
3441 ddt del-float [--pool|-p pool] [--mac|-m mac]
3442 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3443 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3444 ddt disable-float [--pool|-p pool] [--mac|-m mac]
3445 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3446 ddt enable-float [--pool|-p pool] [--mac|-m mac]
3447 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3448 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]
3449 ddt gen-dhcp-file
3450 ddt gen-dns-file [--verbose]
3451 ddt help
3452 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]
3453 ddt remove-pxe [--bootp|-b pxe_config]
3454 ddt remove-tag [--tag|-t tag]
3455 ddt search-mac [--mac|-m mac]
3456 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr]
3457 ddt show-sector [--no-header|-H]
3458 ddt show
3459 ddt show-pool [--no-header|-H]
3460 ddt show-pxe [--no-header|-H]
3461 ddt show-tag [--no-header|-H]
3462 ddt version
3463
3464
3465=head1 DESCRIPTION
3466
3467DDT is an acronym for DHCP-DNS-Tools.
3468The previous command name was not C<ddt> but just C<dhcp-dns-tools>...
3469In practise, DDT is an IP Address Management (IPAM) service.
3470It has been used in the LEGI laboratory for over 10 years.
3471
3472The tool is quite effective and tries to keep things simple
3473but easily configurable for your site like a swiss army knife.
3474Everything is saved in a YAML database
3475and entries could be added, deleted, or modified by the command line.
3476
3477
3478=head1 COMMANDS
3479
3480=head2 add-alias
3481
3482 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]
3483
3484=head2 add-dhcp
3485
3486 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3487
3488=head2 add-float
3489
3490 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3491
3492=head2 add-static
3493
3494 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3495
3496=head2 add-virtual
3497
3498 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]
3499
3500=head2 change-comment
3501
3502 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3503
3504=head2 change-sector
3505
3506 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3507
3508=head2 change-host
3509
3510 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3511
3512=head2 change-ip
3513
3514 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3515
3516=head2 change-mac
3517
3518 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3519
3520=head2 change-tag
3521
3522 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]
3523
3524=head2 change-type
3525
3526 ddt change-type [--hostname|-h hostname] [--sector|-s|-d sector] [--type|-t type]
3527
3528=head2 check-dns
3529
3530 ddt check-dns [--direct] [--reverse]
3531
3532=head2 create-sector
3533
3534 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]
3535
3536=head2 create-pool
3537
3538 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]
3539
3540=head2 create-pxe
3541
3542 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]
3543
3544=head2 create-tag
3545
3546 ddt create-tag [--tag|-t tag] [--comment|-c comment]
3547
3548=head2 del-float
3549
3550 ddt del-float [--pool|-p pool] [--mac|-m mac]
3551
3552=head2 del-pc
3553
3554 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3555
3556=head2 disable-pc
3557
3558 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3559
3560=head2 disable-float
3561
3562 ddt disable-float [--pool|-p pool] [--mac|-m mac]
3563
3564=head2 disable-pxe
3565
3566 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3567
3568=head2 enable-float
3569
3570 ddt enable-float [--pool|-p pool] [--mac|-m mac]
3571
3572=head2 enable-pc
3573
3574 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3575
3576=head2 enable-pxe
3577
3578 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]
3579
3580=head2 gen-dhcp-file
3581
3582 ddt gen-dhcp-file
3583
3584=head2 gen-dns-file
3585
3586 ddt gen-dns-file [--verbose]
3587
3588=head2 help
3589
3590 ddt help
3591
3592=head2 load-database
3593
3594 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]
3595
3596=head2 remove-pxe
3597
3598 ddt remove-pxe [--bootp|-b pxe_config]
3599
3600=head2 remove-tag
3601
3602 ddt remove-tag [--tag|-t tag]
3603
3604=head2 search-mac
3605
3606 ddt search-mac [--mac|-m mac]
3607
3608=head2 sector-add-ip
3609
3610 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr]
3611
3612=head2 show-sector
3613
3614 ddt show-sector [--no-header|-H]
3615
3616=head2 show
3617
3618 ddt show
3619
3620=head2 show-pool
3621
3622 ddt show-pool [--no-header|-H]
3623
3624=head2 show-pxe
3625
3626 ddt show-pxe [--no-header|-H]
3627
3628=head2 show-tag
3629
3630 ddt show-tag [--no-header|-H]
3631
3632=head2 version
3633
3634 ddt version
3635
3636
3637=head1 AUTHORS
3638
3639Written by Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>, Kevin Reverchon, Olivier De-Marchi - Grenoble - France
3640
3641
3642=head1 LICENSE AND COPYRIGHT
3643
3644License GNU GPL version 2 or later and Perl equivalent
3645
3646Copyright (C) 2006-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
Note: See TracBrowser for help on using the repository browser.