source: trunk/ddt/ddt @ 364

Last change on this file since 364 was 364, checked in by g7moreau, 6 years ago
  • Add command change-type (not finish)
  • 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   $mac = get_mac_from_hostname($computer_db, $sector, $hostname, $mac);
1132   control_syntax_mac_address($mac) or exit;
1133
1134   LOOP_ON_COMPUTER:
1135   for my $computer (@{$computer_db->{$sector}}) {
1136      my ($mac_address, $attribute) = %{$computer};
1137
1138      next LOOP_ON_COMPUTER if $mac_address ne $mac;
1139
1140      $attribute->{'address_type'}  = $type;
1141      $attribute->{'modify_time'}   = time;
1142
1143      ipamdb_save("$COMPUTER_YAML", $computer_db);
1144      exit;
1145      }
1146   print "Error: change host $hostname type [FAILED]\n";
1147   }
1148
1149#-------------------------------------------------------------------------------
1150# ACTIVATION section
1151#-------------------------------------------------------------------------------
1152
1153#-------------------------------------------------------------------------------
1154#Nom: disable_pc
1155#Description: désactive une machine (du DHCP ou en IP statique, et du DNS) (champs enabled=non)
1156
1157sub disable_pc {
1158   my ($hostname, $sector, $ip) = @_;
1159
1160   my $computer_db = ipamdb_load($COMPUTER_YAML);
1161
1162   if ($ip ne '') { # disable by IP
1163      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
1164      if ( control_exist_ip($computer_db, $ip) == 1 ) {
1165         die "Error: unkown IP address: $ip [FAILED]\n";
1166         }
1167
1168      for my $sector_current (keys %{$computer_db}) {
1169         next if $sector_current eq 'dset';
1170         next if $sector_current eq 'pool';
1171         next if $sector_current eq 'pxe';
1172         next if $sector_current eq 'tag';
1173         next if $sector_current eq 'version';
1174
1175         my @sectordb = @{$computer_db->{$sector_current}};
1176         LOOP_ON_COMPUTER:
1177         for my $computer (@sectordb) {
1178            my ($mac_address, $attribute) = %{$computer};
1179
1180            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
1181
1182            if ($attribute->{'enabled'} eq 'no') {
1183               print "Info: IP $ip from sector $sector_current is already disable [OK]" .
1184                  " ... Status: $attribute->{'enabled'}\n";
1185               exit;
1186               }
1187
1188            my $timestamp = time;
1189            $attribute->{'modify_time'} = $timestamp;
1190            $attribute->{'enabled'}     = 'no';
1191            ipamdb_save("$COMPUTER_YAML", $computer_db);
1192            print "Info: disabling IP $ip from sector $sector_current [OK]" .
1193               " ... Status: $attribute->{'enabled'}\n";
1194            exit;
1195            }
1196         }
1197      }
1198   else { # disable by Hostname
1199      control_exist_sector($computer_db, $sector);
1200      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
1201         die "Error: unkown host: $hostname, in sector: $sector [FAILED]\n";
1202         }
1203
1204      LOOP_ON_COMPUTER:
1205      for my  $computer (@{$computer_db->{$sector}}) {
1206         my ($mac_address, $attribute) = %{$computer};
1207
1208         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
1209
1210         if ($attribute->{'address_type'} eq 'pool-dhcp') {
1211            die "Error: host $hostname from sector $sector belongs to a a pool [FAILED]" .
1212               " ... use 'disable-float' command instead";
1213            }
1214
1215         if ($attribute->{'enabled'} eq 'no') {
1216            print "Info: host $hostname from sector $sector is already disable [OK]" .
1217               " ... Status: $attribute->{'enabled'}\n";
1218            exit;
1219            }
1220
1221         my $timestamp = time;
1222         $attribute->{'modify_time'} = $timestamp;
1223         $attribute->{'enabled'}     = 'no';
1224         ipamdb_save("$COMPUTER_YAML", $computer_db);
1225         print "Info: disabling host $hostname from sector $sector [OK]" .
1226            " ... Status: $attribute->{'enabled'}\n";
1227         exit;
1228         }
1229      }
1230   }
1231
1232#-------------------------------------------------------------------------------
1233
1234sub disable_float {
1235   my ($pool, $mac) = @_;
1236
1237   my $computer_db = ipamdb_load($COMPUTER_YAML);
1238
1239   if ( control_exist_mac($computer_db, $mac) == 1 ) {
1240      die "Error: unkown physical MAC address: $mac [FAILED]\n";
1241      }
1242
1243   for my $sector_current (keys %{$computer_db}) {
1244      next if $sector_current eq 'dset';
1245      next if $sector_current eq 'pool';
1246      next if $sector_current eq 'pxe';
1247      next if $sector_current eq 'tag';
1248      next if $sector_current eq 'version';
1249
1250      my @sectordb = @{$computer_db->{$sector_current}};
1251
1252      LOOP_ON_COMPUTER:
1253      for my $computer (@sectordb) {
1254         my ($mac_address, $attribute) = %{$computer};
1255         next LOOP_ON_COMPUTER if $mac_address ne $mac;
1256
1257         if ($attribute->{'ip'} eq $pool) {
1258            if ($attribute->{'enabled'} eq 'no') {
1259               print "Info: host $mac from pool $pool is already disable [OK]" .
1260                  " ... Status: $attribute->{'enabled'}\n";
1261               exit;
1262               }
1263            my $timestamp = time;
1264            $attribute->{'modify_time'} = $timestamp;
1265            $attribute->{'enabled'}     = 'no';
1266            ipamdb_save("$COMPUTER_YAML", $computer_db);
1267            print "Info: disabling host $mac from pool $pool [OK]" .
1268               " ... Status: $attribute->{'enabled'}\n";
1269            exit;
1270            }
1271         else {
1272            die "Error: host disable $mac [FAILED]" .
1273               " ... The host $mac does not belong to the $pool pool.\n";
1274            }
1275         }
1276      }
1277   }
1278
1279#-------------------------------------------------------------------------------
1280#Nom: enable_pc
1281#Description: active une machine désactivée(du DHCP ou en IP statique, et du DNS) (champs enabled=non)
1282
1283sub enable_pc {
1284   my ($hostname, $sector, $ip) = @_;
1285
1286   my $computer_db = ipamdb_load($COMPUTER_YAML);
1287
1288   control_exist_sector($computer_db, $sector) or exit;
1289
1290   if ($ip ne '') { # enable by IP
1291      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
1292      if ( control_exist_ip($computer_db, $ip) == 1 ) {
1293         print "Error: unkown IP address: $ip\n";
1294         exit;
1295         }
1296
1297      for my $sector_current (keys %{$computer_db}) {
1298         next if $sector_current eq 'dset';
1299         next if $sector_current eq 'pool';
1300         next if $sector_current eq 'pxe';
1301         next if $sector_current eq 'tag';
1302         next if $sector_current eq 'version';
1303
1304         my @sectordb = @{$computer_db->{$sector_current}};
1305
1306         LOOP_ON_COMPUTER:
1307         for my $computer (@sectordb) {
1308            my ($mac_address, $attribute) = %{$computer};
1309            if ($attribute->{'ip'} eq $ip) {
1310
1311               if ($attribute->{'enabled'} eq 'yes') {
1312                  print "Info: IP $ip belongs to sector $sector is already enable [OK]" .
1313                     " ... Status: $attribute->{'enabled'}\n";
1314                  exit;
1315                  }
1316
1317               my $timestamp = time;
1318               $attribute->{'modify_time'} = $timestamp;
1319               $attribute->{'enabled'}     = 'yes';
1320               ipamdb_save("$COMPUTER_YAML", $computer_db);
1321               print "Info: IP $ip is now enable [OK]" .
1322                  " ... Status: $attribute->{'enabled'}\n";
1323               exit;
1324               }
1325            }
1326         }
1327      }
1328   else { # enable by Hostname
1329      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
1330         die "Error: unkown host: $hostname, in sector: $sector\n";
1331         }
1332
1333      LOOP_ON_COMPUTER:
1334      for my $computer (@{$computer_db->{$sector}}) {
1335         my ($mac_address, $attribute) = %{$computer};
1336         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
1337
1338         if ($attribute->{'address_type'} eq 'pool-dhcp') {
1339            die "Error: host $hostname from sector $sector belongs to a a pool [FAILED]" .
1340               " ... use 'enable-float' command instead";
1341            }
1342
1343         if ($attribute->{'enabled'} eq 'yes') {
1344            print "Info: host $hostname belongs to sector $sector is already enable [OK]" .
1345               " ... Status: $attribute->{'enabled'}\n";
1346            exit;
1347            }
1348
1349         my $timestamp = time;
1350         $attribute->{'modify_time'} = $timestamp;
1351         $attribute->{'enabled'}     = 'yes';
1352         ipamdb_save("$COMPUTER_YAML", $computer_db);
1353         print "Info: host $hostname is now enable [OK]" .
1354            " ... Status: $attribute->{'enabled'}\n";
1355         exit;
1356         }
1357      }
1358   }
1359
1360#-------------------------------------------------------------------------------
1361
1362sub enable_float {
1363   my ($pool, $mac) = @_;
1364
1365   my $computer_db = ipamdb_load($COMPUTER_YAML);
1366
1367   if ( control_exist_mac($computer_db, $mac) == 1 ) {
1368      die "Error: unkown physical MAC address: $mac [FAILED]\n";
1369      }
1370
1371   for my $sector_current (keys %{$computer_db}) {
1372      next if $sector_current eq 'dset';
1373      next if $sector_current eq 'pool';
1374      next if $sector_current eq 'pxe';
1375      next if $sector_current eq 'tag';
1376      next if $sector_current eq 'version';
1377
1378      my @sectordb = @{$computer_db->{$sector_current}};
1379
1380      LOOP_ON_COMPUTER:
1381      for my $computer (@sectordb) {
1382         my ($mac_address, $attribute) = %{$computer};
1383         next LOOP_ON_COMPUTER if $mac_address ne $mac;
1384
1385         if ($attribute->{'ip'} ne $pool) {
1386            die "Error: host enable $mac [FAILED]" .
1387               " ... The host $mac does not belong to the $pool pool.\n";
1388            }
1389
1390         if ($attribute->{'enabled'} eq 'yes') {
1391            print "Info: host $mac from pool $pool is already enable [OK]" .
1392               " ... Status: $attribute->{'enabled'}\n";
1393            exit;
1394            }
1395
1396         my $timestamp = time;
1397         $attribute->{'modify_time'} = $timestamp;
1398         $attribute->{'enabled'}     = 'yes';
1399         ipamdb_save("$COMPUTER_YAML", $computer_db);
1400         print "Info: enabling host $mac from pool $pool [OK]" .
1401            " ... Status: $attribute->{'enabled'}\n";
1402         exit;
1403         }
1404      }
1405   }
1406
1407#-------------------------------------------------------------------------------
1408
1409sub cmd_enable_pc {
1410   local @ARGV = @_;
1411
1412   my $help = get_cmd_name();
1413   my ($hostname, $sector, $ip);
1414
1415   GetOptions(
1416      'hostname|h=s'    => \$hostname,
1417      'sector|s|d=s'    => \$sector,
1418      'ip|i=s'          => \$ip,
1419      );
1420
1421   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1422   exit_on_error_option($help)
1423      if $sector  eq '';
1424   exit_on_error_option($help)
1425      if $hostname   eq ''
1426      and $ip        eq '';
1427   exit_on_error_option($help)
1428      if $hostname   ne ''
1429      and $ip        ne '';
1430
1431   enable_pc($hostname, $sector, $ip);
1432   }
1433
1434#-------------------------------------------------------------------------------
1435
1436sub cmd_disable_pc {
1437   local @ARGV = @_;
1438
1439   my $help = get_cmd_name();
1440   my ($hostname, $sector, $ip);
1441
1442   GetOptions(
1443      'hostname|h=s'    => \$hostname,
1444      'sector|s|d=s'    => \$sector,
1445      'ip|i=s'          => \$ip,
1446      );
1447
1448   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1449   exit_on_error_option($help)
1450      if $sector  eq '';
1451   exit_on_error_option($help)
1452      if $hostname   eq ''
1453      and $ip        eq '';
1454   exit_on_error_option($help)
1455      if $hostname   ne ''
1456      and $ip        ne '';
1457
1458   disable_pc($hostname, $sector, $ip);
1459   }
1460
1461#-------------------------------------------------------------------------------
1462
1463sub cmd_disable_float {
1464   local @ARGV = @_;
1465
1466   my $help = get_cmd_name();
1467   my ($pool, $mac);
1468
1469   GetOptions(
1470      'pool|p=s'  => \$pool,
1471      'mac|m=s'   => \$mac,
1472      );
1473
1474   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
1475   exit_on_error_option($help)
1476      if $pool eq ''
1477      or $mac  eq '';
1478
1479   disable_float($pool, $mac);
1480   }
1481
1482#-------------------------------------------------------------------------------
1483
1484sub cmd_enable_float {
1485   local @ARGV = @_;
1486
1487   my $help = get_cmd_name();
1488   my ($pool, $mac);
1489
1490   GetOptions(
1491      'pool|p=s'  => \$pool,
1492      'mac|m=s'   => \$mac,
1493      );
1494
1495   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
1496   exit_on_error_option($help)
1497      if $pool eq ''
1498      or $mac  eq '';
1499
1500   enable_float($pool, $mac);
1501   }
1502
1503#-------------------------------------------------------------------------------
1504# DELETE section
1505#-------------------------------------------------------------------------------
1506
1507#-------------------------------------------------------------------------------
1508#Nom: del_pc
1509#Description: supprime une machine en DHCP ou en IP statique.
1510
1511sub del_pc {
1512   my ($hostname, $sector, $ip) = @_;
1513
1514   my $computer_db = ipamdb_load($COMPUTER_YAML);
1515
1516   control_exist_sector($computer_db, $sector) or exit;
1517   if ($ip ne '') { # delete by IP
1518      if ( control_exist_ip($computer_db, $ip) == 1 ) {
1519         die "Error: unkown IP address: $ip\n";
1520         }
1521
1522      my $computer_index = 0;
1523
1524      LOOP_ON_COMPUTER:
1525      for my $computer (@{$computer_db->{$sector}}) {
1526         my ($mac_address, $attribute) = %{$computer};
1527
1528         $computer_index++, next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
1529         
1530         splice(@{$computer_db->{$sector}}, $computer_index => 1);
1531         ipamdb_save("$COMPUTER_YAML", $computer_db);
1532         print "Info: host $ip has been removed from the sector $sector [OK]\n";
1533         exit;
1534         }
1535      }
1536   else {
1537      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
1538         die "Error: unkown host: $hostname, in sector: $sector\n";
1539         }
1540
1541      my $computer_index = 0;
1542
1543      LOOP_ON_COMPUTER:
1544      for my $computer (@{$computer_db->{$sector}}) {
1545         my ($mac_address, $attribute) = %{$computer};
1546
1547         $computer_index++, next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
1548
1549         if ($attribute->{'address_type'} eq 'pool-dhcp') {
1550            die "Error: host remove $hostname from the sector $sector [FAILED]" .
1551               " ... The host $hostname belongs to a DHCP pool.\n";
1552            }
1553
1554         splice(@{$computer_db->{$sector}}, $computer_index => 1);
1555         ipamdb_save("$COMPUTER_YAML", $computer_db);
1556         print "Info: host $hostname has been removed from the sector $sector [OK]\n";
1557         exit;
1558         }
1559      }
1560   }
1561
1562#-------------------------------------------------------------------------------
1563#Nom: del_float
1564#Description: supprime une machine d'un pool DHCP
1565
1566sub del_float {
1567   my ($pool, $mac) = @_;
1568
1569   my $computer_db = ipamdb_load($COMPUTER_YAML);
1570
1571   if ( control_exist_mac($computer_db, $mac) == 1 ) {
1572      print "Adresse MAC $mac non trouvée.\n";
1573      exit;
1574      }
1575
1576   for my $sector_current (keys %{$computer_db}) {
1577      next if $sector_current eq 'dset';
1578      next if $sector_current eq 'pool';
1579      next if $sector_current eq 'pxe';
1580      next if $sector_current eq 'tag';
1581      next if $sector_current eq 'version';
1582
1583      my @sectordb = @{$computer_db->{$sector_current}};
1584
1585      my $computer_index = 0;
1586
1587      LOOP_ON_COMPUTER:
1588      for my $computer (@sectordb) {
1589         my ($mac_address, $attribute) = %{$computer};
1590
1591         $computer_index++, next LOOP_ON_COMPUTER if $mac_address ne $mac;
1592
1593         if ($attribute->{'ip'} ne $pool) {
1594            die "Error: host remove $mac [FAILED]" .
1595               " ... The host $mac does not belong to the $pool pool.\n";
1596            }
1597
1598         splice(@{$computer_db->{$sector_current}}, $computer_index => 1);
1599         ipamdb_save("$COMPUTER_YAML", $computer_db);
1600         print "Info: remove host $mac from the pool $pool [OK]\n";
1601         exit;
1602         }
1603      }
1604   }
1605
1606#-------------------------------------------------------------------------------
1607
1608sub cmd_del_pc {
1609   local @ARGV = @_;
1610
1611   my $help = get_cmd_name();
1612   my ($hostname, $sector, $ip);
1613
1614   GetOptions(
1615      'hostname|h=s'    => \$hostname,
1616      'sector|s|d=s'    => \$sector,
1617      'ip|i=s'          => \$ip,
1618      );
1619
1620   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1621   exit_on_error_option($help)
1622      if $sector  eq '';
1623   exit_on_error_option($help)
1624      if $hostname   eq ''
1625      and $ip        eq '';
1626   exit_on_error_option($help)
1627      if $hostname   ne ''
1628      and $ip        ne '';
1629
1630   del_pc($hostname, $sector, $ip);
1631   }
1632
1633#-------------------------------------------------------------------------------
1634
1635sub cmd_del_float {
1636   local @ARGV = @_;
1637
1638   my $help = get_cmd_name();
1639   my ($pool, $mac);
1640
1641   GetOptions(
1642      'pool|p=s'        => \$pool,
1643      'mac|m=s'         => \$mac,
1644      );
1645
1646   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
1647   exit_on_error_option($help)
1648      if $pool eq ''
1649      or $mac  eq '';
1650
1651   del_float($pool, $mac);
1652   }
1653
1654#-------------------------------------------------------------------------------
1655# SECTOR section
1656#-------------------------------------------------------------------------------
1657
1658sub cmd_create_sector {
1659   local @ARGV = @_;
1660
1661   my $help = get_cmd_name();
1662   my ($sector, $dns_extension, $comment);
1663
1664   GetOptions(
1665      'sector|s|d=s'       => \$sector,
1666      'dns-extension|e=s'  => \$dns_extension,
1667      'comment|c=s'        => \$comment,
1668      );
1669
1670   exit_on_error_option($help)
1671      if $sector        eq ''
1672      or $dns_extension eq ''
1673      or $comment       eq '';
1674
1675   $sector =~ m{^[\w\d]}            or die "Error: sector $sector must begin by a letter or a digit\n";
1676   $sector =~ m{^[\w\d][\w\d-_]*$}  or die "Error: sector $sector must only use letter digit dash and underscore character\n";
1677
1678   $comment = normalize_comment($comment);
1679
1680   my $computer_db = ipamdb_load($COMPUTER_YAML);
1681
1682   $computer_db->{'dset'} ||= {};
1683   die "Error: sector already exists: $sector\n" if exists $computer_db->{'dset'}{$sector};
1684
1685   control_syntax_comment($comment)    or exit;
1686
1687   my $timestamp = time;
1688   $computer_db->{'dset'}{$sector} = {
1689      'dns_extension'   => $dns_extension,
1690      'comment'         => $comment,
1691      'create_time'     => $timestamp,
1692      'modify_time'     => $timestamp,
1693      };
1694   $computer_db->{$sector} ||= []; # Create empty sector computer list by default
1695   ipamdb_save("$COMPUTER_YAML", $computer_db);
1696   }
1697
1698#-------------------------------------------------------------------------------
1699
1700sub cmd_sector_add_ip {
1701   local @ARGV = @_;
1702
1703   my $help = get_cmd_name();
1704   my ($sector, $ip_range);
1705
1706   GetOptions(
1707      'sector|s|d=s' => \$sector,
1708      'ip-range|i=s' => \$ip_range,
1709      );
1710
1711   exit_on_error_option($help)
1712      if $sector     eq ''
1713      or $ip_range   eq '';
1714
1715   control_syntax_cidr($ip_range)   or die "Error: bad IP range $ip_range syntax (CIDR)\n";;
1716
1717   my $computer_db = ipamdb_load($COMPUTER_YAML);
1718   exists $computer_db->{'dset'}{$sector} or die "Error: sector not exists: $sector\n";
1719
1720   my $timestamp = time;
1721   $computer_db->{'dset'}{$sector}{'ip_range'} ||= [];
1722   LOOP_ON_CIDR:
1723   for my $cidr_current (@{$computer_db->{'dset'}{$sector}{'ip_range'}}) {
1724      next LOOP_ON_CIDR if $cidr_current ne $ip_range;
1725     
1726      die "Error: IP range $ip_range already in sector $sector\n";
1727      }
1728
1729   my $timestamp = time;
1730   push @{$computer_db->{'dset'}{$sector}{'ip_range'}}, $ip_range;
1731   $computer_db->{'dset'}{$sector}{'modify_time'} = $timestamp;
1732   ipamdb_save("$COMPUTER_YAML", $computer_db);
1733   }
1734
1735#-------------------------------------------------------------------------------
1736# POOL section
1737#-------------------------------------------------------------------------------
1738
1739#-------------------------------------------------------------------------------
1740#Nom: create_pool
1741#Description: crée un pool dans le fichier de données YAML et dans le DHCP.
1742#
1743#Commentaires: il y a un petit bug si jamais on rentre que des adresses ip qui existent déjà.
1744#              Le pool est créé mais sans adresses ip.
1745
1746sub cmd_create_pool {
1747   local @ARGV = @_;
1748
1749   my $help = get_cmd_name();
1750   my ($pool, $sector, $file_pool, $ipaddress_pool);
1751
1752   GetOptions(
1753      'pool|p=s'           => \$pool,
1754      'sector|s|d=s'      => \$sector,
1755      'file-pool|f=s'      => \$file_pool,
1756      'ipaddress-pool|i=s' => \$ipaddress_pool,
1757      );
1758
1759   exit_on_error_option($help)
1760      if $pool             eq ''
1761      or $sector        eq ''
1762      or $file_pool        eq ''
1763      or $ipaddress_pool   eq '';
1764
1765   my $computer_db = ipamdb_load($COMPUTER_YAML);
1766
1767   if ($computer_db->{'pool'}) {
1768      die "Error: pool already exists: $pool\n" if exists $computer_db->{'pool'}{$pool};
1769      }
1770
1771   #--- control if the domain's pool exist ---#
1772   control_exist_sector($computer_db, $sector) or exit;
1773
1774   my @ip_list = ();
1775   #---control if address exist ---#
1776   if ($ipaddress_pool =~ /,/) {
1777      LOOP_ON_IP:
1778      for my $ip (split /,/, $ipaddress_pool) {
1779         if ($ip =~ /-/) {
1780            my ($ip1, $ip2, $ip3, $range) = split /\./, $ip;
1781            my ($first, $last) = split /-/, $range;
1782            for (my $cpt = $first; $cpt <= $last; $cpt++) {
1783               my $ip_loc = "$ip1.$ip2.$ip3.$cpt";
1784               control_syntax_ip($ip_loc) or die "Error: bad IP syntax: $ip_loc\n";
1785               control_exist_ip($computer_db, $ip_loc) or die "Error: IP address already exists: $ip_loc\n";
1786               push @ip_list, $ip_loc;
1787               }
1788            }
1789         else {
1790            control_syntax_ip($ip) or next LOOP_ON_IP;
1791            if ( control_exist_ip($computer_db, $ip) == 0 ) {
1792               print "L'adresse IP $ip existe déjà\n";
1793               next;
1794               }
1795            push @ip_list, $ip;
1796            }
1797         }
1798      }
1799
1800   my $timestamp = time;
1801   $computer_db->{'pool'}{$pool} = {
1802      'ip'          => [@ip_list],
1803      'enabled'     => 'yes',
1804      'create_time' => $timestamp,
1805      'modify_time' => $timestamp,
1806      'file'        => $file_pool,
1807      'domain'      => $sector,
1808      };
1809   ipamdb_save("$COMPUTER_YAML", $computer_db);
1810   }
1811
1812#-------------------------------------------------------------------------------
1813
1814sub cmd_show_pool {
1815   local @ARGV = @_;
1816
1817   my ($no_header);
1818
1819   GetOptions(
1820      'no-header|H' => \$no_header,
1821      );
1822
1823   my $computer_db = ipamdb_load($COMPUTER_YAML);
1824
1825   printf "%-17s %-17s %s\n", 'Pool', 'File', 'DNS-Domain' if not $no_header;
1826   LOOP_ON_PXE:
1827   for my $pool ( keys %{$computer_db->{'pool'}} ) {
1828
1829      printf "%-17s %-17s %s\n",
1830         $pool,
1831         $computer_db->{'pool'}{$pool}{'file'},
1832         $computer_db->{'pool'}{$pool}{'domain'},
1833      }
1834   }
1835
1836#-------------------------------------------------------------------------------
1837# PXE section
1838#-------------------------------------------------------------------------------
1839
1840sub cmd_create_pxe {
1841   local @ARGV = @_;
1842
1843   my $help = get_cmd_name();
1844   my ($pxe_config, $ip_next_server, $filename, $comment);
1845
1846   GetOptions(
1847      'bootp|b=s'       => \$pxe_config,
1848      'next-server|n=s' => \$ip_next_server,
1849      'filename|f=s'    => \$filename,
1850      'comment|c=s'     => \$comment,
1851      );
1852
1853   exit_on_error_option($help)
1854      if $pxe_config       eq ''
1855      or $ip_next_server   eq ''
1856      or $filename         eq ''
1857      or $comment          eq '';
1858
1859   my $computer_db = ipamdb_load($COMPUTER_YAML);
1860
1861   $comment = normalize_comment($comment);
1862
1863   $computer_db->{'pxe'} ||= {};
1864   die "Error: PXE config already exists: $pxe_config\n" if exists $computer_db->{'pxe'}{$pxe_config};
1865
1866   control_syntax_ip($ip_next_server)  or die "Error: bad IP syntax: $ip_next_server\n";
1867   control_syntax_comment($comment)    or exit;
1868
1869   my $timestamp = time;
1870   $computer_db->{'pxe'}{$pxe_config} = {
1871      'ip_next_server'  => $ip_next_server,
1872      'filename'        => $filename,
1873      'comment'         => $comment,
1874      'create_time'     => $timestamp,
1875      'modify_time'     => $timestamp,
1876      };
1877   ipamdb_save("$COMPUTER_YAML", $computer_db);
1878   }
1879
1880#-------------------------------------------------------------------------------
1881
1882sub cmd_remove_pxe {
1883   local @ARGV = @_;
1884
1885   my $help = get_cmd_name();
1886   my ($pxe_config);
1887
1888   GetOptions(
1889      'bootp|b=s' => \$pxe_config,
1890      );
1891
1892   exit_on_error_option($help)
1893      if $pxe_config eq '';
1894
1895   my $computer_db = ipamdb_load($COMPUTER_YAML);
1896
1897   $computer_db->{'pxe'} ||= {};
1898   die "Error: PXE config does not exist: $pxe_config\n" if not exists $computer_db->{'pxe'}{$pxe_config};
1899
1900   # Test if some computer use this config
1901   LOOP_ON_SECTOR:
1902   for my $sector_current (keys %{$computer_db}) {
1903      next if $sector_current eq 'dset';
1904      next if $sector_current eq 'pool';
1905      next if $sector_current eq 'pxe';
1906      next if $sector_current eq 'tag';
1907      next if $sector_current eq 'version';
1908
1909      LOOP_ON_COMPUTER:
1910      for my $computer (@{$computer_db->{$sector_current}}) {
1911         my ($mac_address, $attribute) = %{$computer};
1912
1913         if (exists $attribute->{'pxe_config'}) {
1914            my $hostname = $attribute->{'hostname'};
1915            die "Error: computer still use this PXE config: $hostname.$sector_current $mac_address\n" if $pxe_config eq $attribute->{'pxe_config'};
1916            }
1917         }
1918      }
1919
1920   delete $computer_db->{'pxe'}{$pxe_config};
1921   ipamdb_save("$COMPUTER_YAML", $computer_db);
1922   }
1923
1924#--------------------------------------------------------------------------------
1925
1926sub cmd_show_pxe {
1927   local @ARGV = @_;
1928
1929   my ($no_header);
1930
1931   GetOptions(
1932      'no-header|H' => \$no_header,
1933      );
1934
1935   my $computer_db = ipamdb_load($COMPUTER_YAML);
1936
1937   printf "%-12s %-13s %-30s %s\n", 'PXE-Config', 'Next-Server', 'Filename', 'Comment' if not $no_header;
1938   LOOP_ON_PXE:
1939   for my $pxe_config ( keys %{$computer_db->{'pxe'}} ) {
1940      my $ip_next_server = $computer_db->{'pxe'}{$pxe_config}{'ip_next_server'};
1941      my $filename       = $computer_db->{'pxe'}{$pxe_config}{'filename'};
1942      my $comment        = $computer_db->{'pxe'}{$pxe_config}{'comment'};
1943
1944      printf "%-12s %-13s %-30s %s\n", $pxe_config, $ip_next_server, $filename, $comment;
1945      }
1946   }
1947
1948#-------------------------------------------------------------------------------
1949
1950sub cmd_enable_pxe {
1951   local @ARGV = @_;
1952
1953   my $help = get_cmd_name();
1954   my ($hostname, $sector, $ip, $pxe_config);
1955
1956   GetOptions(
1957      'hostname|h=s'    => \$hostname,
1958      'sector|s|d=s'    => \$sector,
1959      'ip|i=s'          => \$ip,
1960      'bootp|b=s'       => \$pxe_config,
1961      );
1962
1963   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
1964   exit_on_error_option($help)
1965      if $sector  eq ''
1966      or $pxe_config eq '';
1967   exit_on_error_option($help)
1968      if $hostname   eq ''
1969      and $ip        eq '';
1970   exit_on_error_option($help)
1971      if $hostname   ne ''
1972      and $ip        ne '';
1973
1974   my $computer_db = ipamdb_load($COMPUTER_YAML);
1975
1976   die "Error: PXE config not exists: $pxe_config\n" if not exists $computer_db->{'pxe'}{$pxe_config};
1977
1978   control_exist_sector($computer_db, $sector) or exit;
1979   if ($ip ne '') {
1980      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
1981      if ( control_exist_ip($computer_db, $ip) == 1 ) {
1982         die "Error: unkown IP address: $ip\n";
1983         }
1984
1985      for my $sector_current (keys %{$computer_db}) {
1986         next if $sector_current eq 'dset';
1987         next if $sector_current eq 'pool';
1988         next if $sector_current eq 'pxe';
1989         next if $sector_current eq 'tag';
1990         next if $sector_current eq 'version';
1991
1992         LOOP_ON_COMPUTER:
1993         for my $computer (@{$computer_db->{$sector_current}}) {
1994            my ($mac_address, $attribute) = %{$computer};
1995            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
1996
1997            $attribute->{'modify_time'} = time;
1998            $attribute->{'pxe_config'}  = $pxe_config;
1999            ipamdb_save("$COMPUTER_YAML", $computer_db);
2000            print "Info: host $attribute->{'hostname'} ($sector_current), IP $ip, PXE enabled: $pxe_config\n";
2001            exit;
2002            }
2003         }
2004      }
2005   else {
2006      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
2007         die "Error: unkown host: $hostname, in sector: $sector\n";
2008         }
2009
2010      LOOP_ON_COMPUTER:
2011      for my $computer (@{$computer_db->{$sector}}) {
2012         my ($mac_address, $attribute) = %{$computer};
2013         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
2014         
2015         if ($attribute->{'address_type'} eq 'pool-dhcp') {
2016            die "Error. Host $hostname ($sector) in a pool. No PXE possible [FAILED]\n";
2017            }
2018
2019         $attribute->{'modify_time'} = time;
2020         $attribute->{'pxe_config'}  = $pxe_config;
2021         ipamdb_save("$COMPUTER_YAML", $computer_db);
2022         print "Info: host $hostname ($sector), IP $attribute->{'ip'}, PXE enabled: $pxe_config [OK]\n";
2023         exit;
2024         }
2025      }
2026   }
2027
2028#-------------------------------------------------------------------------------
2029
2030sub cmd_disable_pxe {
2031   local @ARGV = @_;
2032
2033   my $help = get_cmd_name();
2034   my ($hostname, $sector, $ip);
2035
2036   GetOptions(
2037      'hostname|h=s'    => \$hostname,
2038      'sector|s|d=s'    => \$sector,
2039      'ip|i=s'          => \$ip,
2040      );
2041
2042   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
2043   exit_on_error_option($help)
2044      if $sector  eq '';
2045   exit_on_error_option($help)
2046      if $hostname   eq ''
2047      and $ip        eq '';
2048   exit_on_error_option($help)
2049      if $hostname   ne ''
2050      and $ip        ne '';
2051
2052   my $computer_db = ipamdb_load($COMPUTER_YAML);
2053
2054   control_exist_sector($computer_db, $sector) or exit;
2055   if ($ip ne '') {
2056      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
2057      if ( control_exist_ip($computer_db, $ip) == 1 ) {
2058         die "Error: unkown IP address: $ip\n";
2059         }
2060
2061      for my $sector_current (keys %{$computer_db}) {
2062         next if $sector_current eq 'dset';
2063         next if $sector_current eq 'pool';
2064         next if $sector_current eq 'pxe';
2065         next if $sector_current eq 'tag';
2066         next if $sector_current eq 'version';
2067
2068         LOOP_ON_COMPUTER:
2069         for my $computer (@{$computer_db->{$sector_current}}) {
2070            my ($mac_address, $attribute) = %{$computer};
2071           
2072            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
2073            next LOOP_ON_COMPUTER if not exists $attribute->{'pxe_config'};
2074
2075            my $pxe_config = $attribute->{'pxe_config'};
2076            $attribute->{'modify_time'} = time;
2077            delete $attribute->{'pxe_config'};
2078            ipamdb_save("$COMPUTER_YAML", $computer_db);
2079            print "Info: IP address: $ip, PXE disable from config: $pxe_config [OK]\n";
2080            exit;
2081            }
2082         }
2083      }
2084   else {
2085      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
2086         die "Error: unkown host: $hostname, in sector: $sector\n";
2087         }
2088
2089      LOOP_ON_COMPUTER:
2090      for my $computer (@{$computer_db->{$sector}}) {
2091         my ($mac_address, $attribute) = %{$computer};
2092
2093         next LOOP_ON_COMPUTER if $attribute->{'hostname'} eq $hostname;
2094
2095         if ($attribute->{'address_type'} eq 'pool-dhcp') {
2096            die "Error: host $hostname ($sector) in a pool. No PXE possible [FAILED]\n";
2097            }
2098
2099         next LOOP_ON_COMPUTER if not exists $attribute->{'pxe_config'};
2100
2101         my $pxe_config = $attribute->{'pxe_config'};
2102         $attribute->{'modify_time'} = time;
2103         delete $attribute->{'pxe_config'};
2104         ipamdb_save("$COMPUTER_YAML", $computer_db);
2105         print "Info: host $hostname ($sector), PXE disable from config: $pxe_config [OK]\n";
2106         exit;
2107         }
2108      }
2109   }
2110
2111#-------------------------------------------------------------------------------
2112# TAG section
2113#-------------------------------------------------------------------------------
2114
2115sub cmd_create_tag {
2116   local @ARGV = @_;
2117
2118   my $help = get_cmd_name();
2119   my ($tag, $comment);
2120
2121   GetOptions(
2122      'tag|t=s'      => \$tag,
2123      'comment|c=s'  => \$comment,
2124      );
2125
2126   exit_on_error_option($help)
2127      if $tag     eq ''
2128      or $comment eq '';
2129
2130   my $computer_db = ipamdb_load($COMPUTER_YAML);
2131
2132   $comment = normalize_comment($comment);
2133
2134   $computer_db->{'tag'} ||= {};
2135   die "Error: TAG already exists: $tag\n" if exists $computer_db->{'tag'}{$tag};
2136
2137   die "Error: TAG 'universal' is intrinsic. It's not possible to create it.\n" if $tag eq 'universal';
2138
2139   if ($tag !~ m/^ \w+ $/xms) {
2140      die "Error: bad format for TAG (alphanumeric string): $tag\n";
2141      }
2142
2143   control_syntax_comment($comment) or exit;
2144
2145   my $timestamp = time;
2146   $computer_db->{'tag'}{$tag} = {
2147      'comment'         => $comment,
2148      'create_time'     => $timestamp,
2149      'modify_time'     => $timestamp,
2150      };
2151   ipamdb_save("$COMPUTER_YAML", $computer_db);
2152   }
2153
2154#-------------------------------------------------------------------------------
2155
2156sub cmd_remove_tag {
2157   local @ARGV = @_;
2158
2159   my $help = get_cmd_name();
2160   my ($tag);
2161
2162   GetOptions(
2163      'tag|t=s' => \$tag,
2164      );
2165
2166   exit_on_error_option($help)
2167      if $tag eq '';
2168
2169   my $computer_db = ipamdb_load($COMPUTER_YAML);
2170
2171   $computer_db->{'tag'} ||= {};
2172   die "Error: TAG does not exist: $tag\n" if not exists $computer_db->{'tag'}{$tag};
2173
2174   # Test if some computer use this config
2175   LOOP_ON_SECTOR:
2176   for my $sector_current (keys %{$computer_db}) {
2177      next if $sector_current eq 'dset';
2178      next if $sector_current eq 'pool';
2179      next if $sector_current eq 'pxe';
2180      next if $sector_current eq 'tag';
2181      next if $sector_current eq 'version';
2182
2183      LOOP_ON_COMPUTER:
2184      for my $computer (@{$computer_db->{$sector_current}}) {
2185         my ($mac_address, $attribute) = %{$computer};
2186
2187         if (exists $attribute->{'tag'}) {
2188            my $hostname = $attribute->{'hostname'};
2189            die "Error: computer still use this TAG: $hostname.$sector_current $mac_address\n" if $tag eq $attribute->{'tag'};
2190            }
2191         }
2192      }
2193
2194   delete $computer_db->{'tag'}{$tag};
2195   ipamdb_save("$COMPUTER_YAML", $computer_db);
2196   }
2197
2198#--------------------------------------------------------------------------------
2199
2200sub cmd_show_tag {
2201   local @ARGV = @_;
2202
2203   my ($no_header);
2204
2205   GetOptions(
2206      'no-header|H' => \$no_header,
2207      );
2208
2209   my $computer_db = ipamdb_load($COMPUTER_YAML);
2210
2211   printf "%-12s %s\n", 'TAG', 'Comment' if not $no_header;
2212   LOOP_ON_TAG:
2213   for my $tag ( keys %{$computer_db->{'tag'}} ) {
2214      my $comment = $computer_db->{'tag'}{$tag}{'comment'};
2215
2216      printf "%-12s %s\n", $tag, $comment;
2217      }
2218   }
2219
2220#--------------------------------------------------------------------------------
2221# GLOBAL section
2222#--------------------------------------------------------------------------------
2223
2224sub cmd_upgrade_db {
2225   my $flag_change;
2226
2227   my $computer_db = ipamdb_load($COMPUTER_YAML);
2228
2229   LOOP_ON_SECTOR:
2230   for my $sector_current (keys %{$computer_db}) {
2231      next if $sector_current eq 'dset';
2232      next if $sector_current eq 'pool';
2233      next if $sector_current eq 'pxe';
2234      next if $sector_current eq 'tag';
2235      next if $sector_current eq 'version';
2236
2237      my @sectordb = @{$computer_db->{$sector_current}};
2238
2239      LOOP_ON_COMPUTER:
2240      for my $computer (@sectordb) {
2241         my ($mac_address, $attribute) = %{$computer};
2242         my $new_mac = normalize_mac_address($mac_address);
2243         print "perl -pi -e 's/$mac_address:/$new_mac:/' $COMPUTER_YAML\n" if "$mac_address" ne "$new_mac";
2244
2245         my $comment = $attribute->{'comment'};
2246         $comment =~ s/\s\s+/ /g and $flag_change++;
2247         $comment =~ s/^\s+\S//  and $flag_change++;
2248         $comment =~ s/\S\s+$//  and $flag_change++;
2249         $comment =~ s{^(\d\d\d\d)\/O(\d\/\d\d)}{$1/0$2} and $flag_change++;
2250         $comment =~ s{^(\d\d\d\d\/\d\d\/)O(\d)}{$1/0$2} and $flag_change++;
2251         $comment =~ s{^(\d\d\d\d)\/(\d\d)\/(\d\d)}{$1-$2-$3} and $flag_change++;
2252         if ($comment !~ m/^\d\d\d\d-\d\d-\d\d/) {
2253            print "# no date at beginning of comment $mac_address\n";
2254            }
2255
2256         $attribute->{'comment'} = $comment;
2257         }
2258      }
2259   print "# FLAG :$flag_change\n";
2260
2261   ipamdb_save("$COMPUTER_YAML", $computer_db) if $flag_change;
2262   }
2263
2264#--------------------------------------------------------------------------------
2265
2266sub cmd_show_sector {
2267   local @ARGV = @_;
2268
2269   my ($no_header);
2270
2271   GetOptions(
2272      'no-header|H' => \$no_header,
2273      );
2274
2275   my $computer_db = ipamdb_load($COMPUTER_YAML);
2276
2277   my $tb_computer = Text::Table->new(
2278     {align  => 'left',   align_title => 'left',   title => 'Sector'},
2279     {is_sep => 1,        body        => '  '},
2280     {align  => 'left',   align_title => 'left',   title => 'DNS-Extension'},
2281     {is_sep => 1,        body        => '  '},
2282     {align  => 'left',   align_title => 'left',   title => 'IP-Range'},
2283     {align  => 'left',   align_title => 'left',   title => 'Date'},
2284     {align  => 'left',   align_title => 'left',   title => 'Comment'},
2285     {align  => 'left',   align_title => 'left',   title => 'Category'},
2286     );
2287
2288   LOOP_ON_SECTOR:
2289   for my $sector_current (sort keys %{$computer_db}) {
2290      next if $sector_current eq 'dset';
2291      next if $sector_current eq 'pool';
2292      next if $sector_current eq 'pxe';
2293      next if $sector_current eq 'tag';
2294      next if $sector_current eq 'version';
2295
2296      $tb_computer->add($sector_current), next LOOP_ON_SECTOR if not exists $computer_db->{'dset'}{$sector_current};
2297
2298      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computer_db->{'dset'}{$sector_current}{'modify_time'};
2299      $year += 1900;
2300      $mon++;
2301      my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;
2302
2303      my $ip_range;
2304      $ip_range = join ',', @{$computer_db->{'dset'}{$sector_current}{'ip_range'}} if exists $computer_db->{'dset'}{$sector_current}{'ip_range'};
2305
2306      my $category;
2307      my $comment = $computer_db->{'dset'}{$sector_current}{'comment'};
2308      $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
2309      $comment =~ s/\s+(\(\w+\))$// and $category = $1;
2310
2311      $tb_computer->add($sector_current,
2312         $computer_db->{'dset'}{$sector_current}{'dns_extension'},
2313         $ip_range,
2314         $date,
2315         $comment,
2316         $category,
2317         );
2318      }
2319
2320   print $tb_computer->title(),
2321         $tb_computer->rule('-') if not $no_header;
2322   print $tb_computer->body();
2323   }
2324
2325#--------------------------------------------------------------------------------
2326
2327sub cmd_search_mac {
2328   local @ARGV = @_;
2329
2330   my $help = get_cmd_name();
2331   my ($mac);
2332
2333   GetOptions(
2334      'mac|m=s' => \$mac,
2335      );
2336
2337   exit_on_error_option($help)
2338      if $mac eq '';
2339
2340   $mac = normalize_mac_address($mac);
2341
2342   my $computer_db = ipamdb_load($COMPUTER_YAML);
2343
2344   control_syntax_mac_address($mac) or exit;
2345
2346   LOOP_ON_SECTOR:
2347   for my $sector_current (keys %{$computer_db}) {
2348      next if $sector_current eq 'dset';
2349      next if $sector_current eq 'pool';
2350      next if $sector_current eq 'pxe';
2351      next if $sector_current eq 'tag';
2352      next if $sector_current eq 'version';
2353
2354      my @sectordb = @{$computer_db->{$sector_current}};
2355
2356      LOOP_ON_COMPUTER:
2357      for my $computer (@sectordb) {
2358         my ($mac_address, $attribute) = %{$computer};
2359
2360         next LOOP_ON_COMPUTER if $mac_address ne $mac;
2361
2362         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $attribute->{'modify_time'};
2363         $year += 1900;
2364         $mon++;
2365         my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;
2366
2367         my $comment = $attribute->{'comment'};
2368         $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
2369
2370         my $enable = $attribute->{'enabled'};
2371         if (exists $attribute->{'pxe_config'}) {
2372            $enable .= '/' . $attribute->{'pxe_config'};
2373            }
2374         if (exists $attribute->{'tag'}) {
2375            $enable .= ':' . $attribute->{'tag'};
2376            }
2377
2378         printf "%-30s  %-20s %17s %9s %3s %10s %s\n",
2379            $attribute->{'hostname'} . '.' . $sector_current,
2380            $attribute->{'ip'},
2381            $mac_address,
2382            $attribute->{'address_type'},
2383            $enable,
2384            $date,
2385            $comment;
2386         }
2387      }
2388   }
2389
2390#--------------------------------------------------------------------------------
2391#Nom: show
2392#Description: liste les machines à partir du fichier YAML par nom de domaine.
2393
2394sub cmd_show_host {
2395   my %ipdb = ();
2396
2397   my $computer_db = ipamdb_load($COMPUTER_YAML);
2398
2399   my $tb_computer = Text::Table->new(
2400     {align  => 'left',   align_title => 'left',   title => 'Hostname.Sector'},
2401     {is_sep => 1,        body        => '  '},
2402     {align  => 'left',   align_title => 'left',   title => 'IPv4-Address'},
2403     {is_sep => 1,        body        => '  '},
2404     {align  => 'center', align_title => 'center', title => 'MAC-Address'},
2405     {is_sep => 1,        body        => '  '},
2406     {align  => 'right',  align_title => 'right',  title => 'Type'},
2407     {align  => 'right',  align_title => 'right',  title => 'Status'},
2408     {is_sep => 1,        body        => '  '},
2409     {align  => 'left',   align_title => 'left',   title => 'Date'},
2410     {align  => 'left',   align_title => 'left',   title => 'Comment'},
2411     );
2412
2413  LOOP_ON_SECTOR:
2414   for my $sector_current (sort keys %{$computer_db}) {
2415      next if $sector_current eq 'dset';
2416      next if $sector_current eq 'pool';
2417      next if $sector_current eq 'pxe';
2418      next if $sector_current eq 'tag';
2419      next if $sector_current eq 'version';
2420
2421      my @sectordb = @{$computer_db->{$sector_current}};
2422
2423      LOOP_ON_COMPUTER:
2424      for my $computer (@sectordb) {
2425         my ($mac_address, $attribute) = %{$computer};
2426         my $ip = $attribute->{'ip'};
2427
2428         if ($ip =~ m/$DDT::RE::IPv4_ADDRESS/xms) {
2429            if ( not exists $ipdb{$ip} ) {
2430               $ipdb{$ip} = {
2431                  'mac_address'  => $mac_address,
2432                  %{$attribute},
2433                  'sector'    => $sector_current,
2434                  };
2435               }
2436            else {
2437               print {*STDERR} "# Warning: $ip already exists in the database with MAC $mac_address!\n";
2438               }
2439            next LOOP_ON_COMPUTER;
2440            }
2441
2442         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $attribute->{'modify_time'};
2443         $year += 1900;
2444         $mon++;
2445         my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;
2446
2447         my $comment = normalize_comment($attribute->{'comment'});
2448         $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
2449
2450         my $enable = $attribute->{'enabled'};
2451         if (exists $attribute->{'pxe_config'}) {
2452            $enable .= '/' . $attribute->{'pxe_config'};
2453            }
2454         if (exists $attribute->{'tag'}) {
2455            $enable .= ':' . $attribute->{'tag'};
2456            }
2457
2458         #printf "%-30s  %-20s %17s %9s %3s %10s %s\n",
2459         $tb_computer->add(
2460            $attribute->{'hostname'} . '.' . $sector_current,
2461            $ip,
2462            $mac_address,
2463            $attribute->{'address_type'},
2464            $enable,
2465            $date,
2466            $comment,
2467            );
2468         }
2469      #print "\n# *** List of pool computers in the sector: $sector_current ***\n";
2470      }
2471
2472   #print "\n# *** List of computers ordered by IP and sector ***\n";
2473   LOOP_ON_IP_ADDRESS:
2474   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %ipdb)) {
2475      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $ipdb{$ip}->{'modify_time'};
2476      $year += 1900;
2477      $mon++;
2478      my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;
2479
2480      my $comment =$ipdb{$ip}->{'comment'};
2481      $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
2482
2483      my $enable = $ipdb{$ip}->{'enabled'};
2484      if (exists $ipdb{$ip}->{'pxe_config'}) {
2485         $enable .= '/' . $ipdb{$ip}->{'pxe_config'};
2486         }
2487      if (exists $ipdb{$ip}->{'tag'}) {
2488         $enable .= ':' . $ipdb{$ip}->{'tag'};
2489         }
2490
2491      #printf "%-30s %-20s %17s %9s %3s %10s %s\n",
2492      $tb_computer->add(
2493         $ipdb{$ip}->{'hostname'} . '.' . $ipdb{$ip}->{'sector'},
2494         $ip,
2495         normalize_mac_address($ipdb{$ip}->{'mac_address'}),
2496         $ipdb{$ip}->{'address_type'},
2497         $enable,
2498         $date,
2499         $comment
2500         );
2501      }
2502
2503   print $tb_computer->title();
2504   print $tb_computer->rule('-');
2505   print $tb_computer->body();
2506   }
2507
2508#-------------------------------------------------------------------------------
2509#Nom: cmd_generate_dhcp_file
2510#Description: génère les fichiers de configuration des machines et des pools du dhcp
2511
2512sub cmd_generate_dhcp_file {
2513   backup_database();
2514
2515   my $computer_db = ipamdb_load($COMPUTER_YAML);
2516
2517   my %file_pool;
2518
2519   for my $sector_current (keys %{$computer_db}) {
2520      next if $sector_current eq 'dset';
2521      next if $sector_current eq 'pool';
2522      next if $sector_current eq 'pxe';
2523      next if $sector_current eq 'tag';
2524      next if $sector_current eq 'version';
2525
2526      open FILE_VLAN, '>', "$FOLDER_GEN_DHCP/$sector_current";
2527      my @sectordb = @{$computer_db->{$sector_current}};
2528      for my $value (@sectordb) {
2529         ALL_MAC_ADDRESS:
2530         for my $mac_addres (keys %{$value}) {
2531            #host pcdavoust {  deny-unknown-clients;
2532            #hardware ethernet 0:6:5b:b8:13:d1;
2533            #fixed-address 194.254.66.72;
2534            #}
2535
2536            my $hostname     = $value->{$mac_addres}{'hostname'};
2537            my $ip           = $value->{$mac_addres}{'ip'};
2538            my $comment      = $value->{$mac_addres}{'comment'};
2539            my $address_type = $value->{$mac_addres}{'address_type'};
2540            my $enabled      = $value->{$mac_addres}{'enabled'};
2541            my $tags         = $value->{$mac_addres}{'tag'} || 'universal';
2542
2543            my $buffer;
2544            if ($address_type eq 'dhcp') {
2545               if ($enabled eq 'yes') {
2546                  $buffer  = "host $hostname {\n"; # deny-unknown-clients;
2547                  $buffer .= "   hardware ethernet $mac_addres;\n";
2548                  $buffer .= "   fixed-address $ip;\n";
2549
2550                  if (exists $value->{$mac_addres}{'pxe_config'}) {
2551                     my $pxe_config     = $value->{$mac_addres}{'pxe_config'};
2552                     my $ip_next_server = $computer_db->{'pxe'}{$pxe_config}{'ip_next_server'};
2553                     my $filename       = $computer_db->{'pxe'}{$pxe_config}{'filename'};
2554                     $buffer .= "   next-server $ip_next_server;\n";
2555                     $buffer .= "   filename \"$filename\";\n";
2556                     }
2557                  $buffer .= "   #comment: $comment\n";
2558                  $buffer .= "   }\n";
2559                  $buffer .= "\n";
2560
2561                  for my $tag (split/,/, $tags) {
2562                     $file_pool{"tag-$tag"} ||= [];
2563                     push @{$file_pool{"tag-$tag"}}, "subclass \"tag-$tag\" 1:$mac_addres; # $comment\n";
2564                     }
2565                  }
2566               else {
2567                  $buffer  = "#host $hostname {\n"; # deny-unknown-clients;
2568                  $buffer .= "#   hardware ethernet $mac_addres;\n";
2569                  $buffer .= "#   fixed-address $ip;\n";
2570                  $buffer .= "#   comment: $comment \n";
2571                  $buffer .= "#   }\n";
2572                  $buffer .= "\n";
2573                  }
2574               print FILE_VLAN $buffer;
2575               }
2576            elsif ($address_type eq 'pool-dhcp') {
2577               #--- Génère les fichiers pool dhcp ---#
2578               for my $current_pool (keys %{$computer_db->{'pool'}}) {
2579                  next if $current_pool ne $ip;
2580
2581                  if ($enabled eq 'yes') {
2582                     $buffer = "subclass \"$current_pool\" 1:$mac_addres; # $comment\n";
2583
2584                     for my $tag (split/,/, $tags) {
2585                        $file_pool{"tag-$tag"} ||= [];
2586                        push @{$file_pool{"tag-$tag"}}, "subclass \"tag-$tag\" 1:$mac_addres; # $comment\n";
2587                        }
2588                     }
2589                  else {
2590                     $buffer = "#subclass \"$current_pool\" 1:$mac_addres; # $comment\n";
2591                     }
2592
2593                  my $current_pool_file_name = $computer_db->{'pool'}{$current_pool}{'file'};
2594
2595                  $file_pool{$current_pool_file_name} ||= [];
2596                  push @{$file_pool{$current_pool_file_name}}, $buffer;
2597                  }
2598               }
2599            }
2600         }
2601
2602      close FILE_VLAN;
2603
2604      for my $file_name (keys %file_pool) {
2605         open FILE_POOL, '>', "$FOLDER_GEN_DHCP/$file_name";
2606         print FILE_POOL @{$file_pool{$file_name}};
2607         close FILE_POOL;
2608         }
2609      }
2610      print "Copy DHCP files from $FOLDER_GEN_DHCP to /etc/dhcp/include/\n";
2611      exec $SCRIPT_UPDATE;
2612   }
2613
2614#-------------------------------------------------------------------------------
2615#Nom: cmd_generate_dns_file
2616#Description: génère les fichiers d'enregistrements DNS
2617
2618sub cmd_generate_dns_file {
2619   local @ARGV = @_;
2620
2621   my $help = get_cmd_name();
2622   my ($verbose);
2623
2624   GetOptions(
2625      'verbose|v' => \$verbose,
2626      );
2627
2628   my $buffer_fwd;
2629   my $buffer_rev;
2630   my $pool_domain;
2631
2632   my $computer_db = ipamdb_load($COMPUTER_YAML);
2633
2634   for my $sector_current (keys %{$computer_db}) {
2635      next if $sector_current eq 'dset';
2636      next if $sector_current eq 'pool';
2637      next if $sector_current eq 'pxe';
2638      next if $sector_current eq 'tag';
2639      next if $sector_current eq 'version';
2640
2641      if ($sector_current eq 'pool') {
2642         LOOP_ON_COMPUTER:
2643         for my $computer (@{$computer_db->{$sector_current}}) {
2644            for my $pool_name (keys %{$computer}) {
2645               $pool_domain = $computer->{$pool_name}->{'domain'}."\n";
2646               #print $computer->{$pool_name}->{'file'};
2647               chomp $pool_domain;
2648               open FILE_FORWARD_DNS, '>>', "$FOLDER_GEN_DNS/db.$pool_domain.fwd";
2649               open FILE_REVERSE_DNS, '>>', "$FOLDER_GEN_DNS/db.$pool_domain.rev";
2650               my @T_pool_ip = @{$computer->{$pool_name}->{'ip'}};
2651               for my $pool_ip (@T_pool_ip) {
2652                  my @T_split = split(/\./ , $pool_ip);
2653                  $buffer_fwd = sprintf "%-24s IN  A  %-15s ;\n", "$pool_name$T_split[3]", $pool_ip;
2654                  $buffer_rev = "$T_split[3]   IN PTR   $pool_name$T_split[3].$pool_domain.\n";
2655                  print FILE_FORWARD_DNS $buffer_fwd;
2656                  print FILE_REVERSE_DNS $buffer_rev;
2657                  }
2658               close FILE_FORWARD_DNS;
2659               close FILE_REVERSE_DNS;
2660               }
2661            }
2662         }
2663
2664      else {
2665         #--- Création du fichier non-reverse ---#
2666         open FILE_FORWARD_DNS, ">> $FOLDER_GEN_DNS/db.$sector_current.fwd";
2667         open FILE_REVERSE_DNS, ">> $FOLDER_GEN_DNS/db.$sector_current.rev";
2668
2669         my @sectordb = @{$computer_db->{$sector_current}};
2670
2671         LOOP_ON_COMPUTER:
2672         for my $computer (@sectordb) {
2673            my ($mac_address, $attribute) = %{$computer};
2674
2675            #host pcdavoust {  deny-unknown-clients;
2676            #hardware ethernet 0:6:5b:b8:13:d1;
2677            #fixed-address 194.254.66.72;
2678            #}
2679
2680            my $hostname     = $attribute->{'hostname'};
2681            my $ip           = $attribute->{'ip'};
2682            my $comment      = $attribute->{'comment'};
2683            my $address_type = $attribute->{'address_type'};
2684            my $enabled      = $attribute->{'enabled'};
2685
2686            next LOOP_ON_COMPUTER if not (($address_type eq 'dhcp') or ($address_type eq 'static'));
2687
2688            my $dns_domain = $sector_current;
2689            if (exists $computer_db->{'dset'}{$sector_current}) {
2690               $dns_domain = $computer_db->{'dset'}{$sector_current}{'dns_extension'};
2691               }
2692
2693            my @ip_split = split /\./, $ip;
2694            if ($enabled eq 'yes') {
2695               if (exists $attribute->{'dns_extension'}
2696                     and "$attribute->{'dns_extension'}" != "$dns_domain") {
2697                  print "A FAIRE\n";
2698                  }
2699               $buffer_fwd = sprintf "%-24s  IN A   %-15s ; %s\n", $hostname, $ip, $comment;
2700               $buffer_rev = sprintf "%3i    IN PTR %-15s\n", $ip_split[3], "$hostname.$dns_domain.";
2701               }
2702
2703            else {
2704               $buffer_fwd = sprintf ";%-24s IN A   %-15s ; %s\n", $hostname, $ip, $comment;
2705               $buffer_rev = sprintf ";%3i   IN PTR %-15s\n", $ip_split[3], "$hostname.$dns_domain.";
2706               }
2707            print FILE_REVERSE_DNS $buffer_rev;
2708            print FILE_FORWARD_DNS $buffer_fwd;
2709            }
2710         close FILE_REVERSE_DNS;
2711         close FILE_FORWARD_DNS;
2712         print "- DNS: db.$sector_current.fwd db.$sector_current.rev [CREATE].\n" if $verbose;
2713         print "  Ex : sort -k 4n -t . $FOLDER_GEN_DNS/db.$sector_current.fwd\n"     if $verbose;
2714         }
2715      }
2716   }
2717
2718#--------------------------------------------------------------------------------
2719
2720sub shell_command {
2721   my $cmd = shift;
2722
2723   require FileHandle;
2724   my $fh     = new FileHandle;
2725   my @result = ();
2726   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
2727   @result = <$fh>;
2728   close $fh;
2729   chomp @result;
2730   return @result;
2731   }
2732
2733#--------------------------------------------------------------------------------
2734
2735sub cmd_check_dns {
2736   local @ARGV = @_;
2737
2738   my $help = get_cmd_name();
2739   my ($opt_direct, $opt_reverse, $opt_byip);
2740
2741   GetOptions(
2742      'direct|d'  => \$opt_direct,
2743      'reverse|r' => \$opt_reverse,
2744      'by-ip|b'   => \$opt_byip,
2745      );
2746
2747   my $computer_db = ipamdb_load($COMPUTER_YAML);
2748
2749   if ($opt_direct or not ($opt_reverse or $opt_byip)) { # DDT to DNS check
2750      LOOP_ON_SECTOR:
2751      for my $sector_current (keys %{$computer_db}) {
2752         next if $sector_current eq 'dset';
2753         next if $sector_current eq 'pool';
2754         next if $sector_current eq 'pxe';
2755         next if $sector_current eq 'tag';
2756         next if $sector_current eq 'version';
2757
2758         my @sectordb = @{$computer_db->{$sector_current}};
2759
2760         LOOP_ON_COMPUTER:
2761         for my $computer (@sectordb) {
2762            my ($mac_address, $attribute) = %{$computer};
2763            #my $new_mac = normalize_mac_address($mac_address);
2764            my $ip = $attribute->{'ip'};
2765            next LOOP_ON_COMPUTER if not $ip =~ m/$DDT::RE::IPv4_ADDRESS/xms;
2766            next LOOP_ON_COMPUTER if $attribute->{'enabled'} eq 'no';
2767
2768            my $dns_hostname_fq = scalar gethostbyaddr(inet_aton($ip), AF_INET);
2769            my ($dns_hostname) = split /\./, $dns_hostname_fq;
2770
2771            if ($attribute->{'hostname'} ne $dns_hostname) {
2772               print "$mac_address ($sector_current) $ip - $dns_hostname / $attribute->{'hostname'} # $attribute->{'comment'}\n";
2773               next LOOP_ON_COMPUTER;
2774               }
2775
2776            my $packed_ip = scalar gethostbyname($dns_hostname_fq);
2777            if (defined $packed_ip) {
2778               my $ip_address = inet_ntoa($packed_ip);
2779               if ($ip ne $ip_address) {
2780                  print "Error: bad IP $ip for reverse DNS on $dns_hostname_fq\n";
2781                  next LOOP_ON_COMPUTER;
2782                  }
2783               }
2784            }
2785         }
2786      }
2787
2788   if ($opt_reverse) {  # DNS to DDT check
2789      my %saw; # count for unique member
2790      my @dns_domain_list = sort grep !$saw{$_}++,
2791         map $computer_db->{'dset'}{$_}{'dns_extension'},
2792         grep exists($computer_db->{'dset'}{$_}{'dns_extension'}),
2793         keys $computer_db->{'dset'};
2794      LOOP_ON_DNS:
2795      for my $dns (@dns_domain_list) {
2796         LOOP_ON_IP:
2797         for (shell_command("host -t A -l $dns")) {
2798            # smtp2.legi.grenoble-inp.fr has address 194.254.67.37
2799            next if not m/has address/;
2800            next if not m/^(\w[\w-_\.]+\w)\s+has\saddress\s+(\d[\d\.]+\d)$/;
2801            my ($hostname_fq, $ip) = ($1, $2);
2802            control_syntax_ip($ip) or next LOOP_ON_IP;
2803            if (control_exist_ip($computer_db, $ip) == 1) {
2804               printf "Unkown IP: %-15s / %s\n", $ip, $hostname_fq;
2805               next LOOP_ON_IP;
2806               }
2807            }
2808         }
2809      }
2810
2811   if ($opt_byip) {  # IP Range DDT check
2812      my @ip_check;
2813      LOOP_ON_SECTOR:
2814      for my $sector_current (keys %{$computer_db->{'dset'}}) {
2815         next LOOP_ON_SECTOR if not exists $computer_db->{'dset'}{$sector_current}{'ip_range'};
2816
2817         LOOP_ON_CIDR:
2818         for my $ip_range (@{$computer_db->{'dset'}{$sector_current}{'ip_range'}}) {
2819
2820            LOOP_ON_IP:
2821            for my $ip (NetAddr::IP->new($ip_range)->hostenum()) {
2822               $ip =~ s{/32$}{};
2823               
2824               my $dns_hostname_fq = scalar gethostbyaddr(inet_aton($ip), AF_INET);
2825               my ($dns_hostname) = split /\./, $dns_hostname_fq;
2826
2827               # Verify reverse return same IP
2828               my $packed_ip = scalar gethostbyname($dns_hostname_fq);
2829               if (defined $packed_ip) {
2830                  my $ip_address = inet_ntoa($packed_ip);
2831                  if ($ip ne $ip_address) {
2832                     print "Error: bad IP $ip for reverse DNS on $dns_hostname_fq sector $sector_current\n";
2833                     }
2834                  }
2835
2836               # Verify direct return same hostname
2837               LOOP_ON_COMPUTER:
2838               for my $computer (@{$computer_db->{$sector_current}}) {
2839                  my ($mac_address, $attribute) = %{$computer};
2840
2841                  next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
2842
2843                  print "Error: bad DNS host $dns_hostname / DDT host $attribute->{'hostname'} in sector $sector_current with IP $ip\n"
2844                     if $attribute->{'hostname'} ne $dns_hostname and $attribute->{'enabled'} ne 'no';
2845
2846                  next LOOP_ON_IP;
2847                  }
2848
2849               # Declare in DNS but not in DDT
2850               print "Error: DNS host $dns_hostname in sector $sector_current with IP $ip not declare in DDT database\n" if $dns_hostname ne '';
2851               }
2852            }
2853         }
2854      }
2855   }
2856
2857#-------------------------------------------------------------------------------
2858#Nom: load_data_dhcp
2859#Description: permet de charger le fichier de données YAML via les fichiers de configuration
2860#             machines.
2861#            ATTENTION: LES COMMENTAIRES DU FICHIER DISPARAITRONT.
2862
2863sub load_data_dhcp {
2864   my ($sector, $input_file) = @_;
2865
2866   my $computer_db = ipamdb_load($COMPUTER_YAML);
2867
2868   my @T_mac;
2869   my @T_host;
2870   my @T_ip;
2871   my $cpt;
2872   open (FILE, "<$input_file");
2873   my @buffer = <FILE>;
2874   close(FILE);
2875
2876   LINE:
2877   for my $ligne (@buffer) {
2878      #--
2879      $ligne =~ s/#.*$//;
2880      $ligne =~ s/\s+/ /;
2881      $ligne =~ s/^\s+//;
2882      next if $ligne eq '';
2883
2884      if ($ligne =~ /^host /) {
2885         $cpt=0;
2886         my @T_split = split(/host\s+/, $ligne);
2887         @T_host = split(/ /, $T_split[1]);
2888         chomp($T_host[0]);
2889
2890         $cpt++;
2891         }
2892
2893      if ($ligne =~ /^*ethernet /) {
2894         $ligne =~ s/;//g;
2895         @T_mac = split(/ethernet\s+/, $ligne);
2896         chomp($T_mac[1]);
2897         $cpt++;
2898         }
2899
2900      if ($ligne =~ /^*address /) {
2901         $ligne =~ s/;//g;
2902         @T_ip = split(/address\s+/, $ligne);
2903         chomp($T_ip[1]);
2904
2905         $cpt++;
2906         }
2907
2908      if ($cpt == 3) {
2909         #   print "MAC $T_mac[1] HOST $T_host[0] IP $T_ip[1].\n";
2910         my $mac = $T_mac[1];
2911         my $hostname = $T_host[0];
2912         my $ip = $T_ip[1];
2913         $cpt = 0;
2914
2915         if ( control_exist_hostname($computer_db, $sector, $hostname) == 0 ) {
2916            print "Error: host already exist in sector $sector: $hostname\n";
2917            next LINE;
2918            }
2919         control_syntax_mac_address($mac) or next LINE;
2920         if ( control_exist_mac($computer_db, $mac) == 0) {
2921            print "Error: physical MAC address already exists: $mac\n";
2922            next LINE;
2923            }
2924
2925         control_syntax_ip($ip) or next LINE;
2926         if ( control_exist_ip($computer_db, $ip) == 0 ) {
2927            print "Error: IP address already exists: $ip\n";
2928            next LINE;
2929            }
2930         my $timestamp = time;
2931         push @{$computer_db->{$sector}}, { $mac => {
2932            'hostname'     => $hostname,
2933            'ip'           => $ip,
2934            'address_type' => 'dhcp',
2935            'enabled'      => 'yes',
2936            'create_time'  => $timestamp,
2937            'modify_time'  => $timestamp,
2938            'alias'        =>  '',
2939            }};
2940         }
2941      }
2942   }
2943
2944#-------------------------------------------------------------------------------
2945#Nom: load_data_pool
2946#Description: permet de charger le fichier YAML via les fichiers de conf 'pool' du dhcp.
2947
2948sub load_data_pool {
2949   my ($sector, $input_file) = @_;
2950
2951   my @T_mac;
2952
2953   open (FILE, "<$input_file");
2954   my @buffer = <FILE>;
2955   close(FILE);
2956
2957   my $computer_db = ipamdb_load($COMPUTER_YAML);
2958
2959   for my $ligne (@buffer) {
2960      #--
2961      $ligne =~ s/#.*$//;
2962      $ligne =~ s/\s+/ /;
2963      $ligne =~ s/^\s+//;
2964      $ligne =~ s/;//g;
2965      $ligne =~ s/"//g;
2966      next if $ligne eq '';
2967
2968      if (($ligne =~ /^subclass/)) {
2969         my @T_split = split(/ / ,$ligne);
2970         my $pool = $T_split[1];
2971
2972         @T_mac = split(/:/ , $T_split[2]);
2973         my $mac = $T_mac[1].":".$T_mac[2].":".$T_mac[3].":".$T_mac[4].":".$T_mac[5].":".$T_mac[6];
2974         control_syntax_mac_address($mac) or next;
2975         if (control_exist_mac($computer_db, $mac) == 0) {
2976            print "Error: physical MAC address already exists: $mac\n";
2977            next;
2978            }
2979
2980         #--- cette partie teste si le pool existe.
2981         if (not exists $computer_db->{'pool'}{$pool}) {
2982            print "Error: create pool with create_pool command before load database: $pool\n";
2983            exit;
2984            }
2985
2986         if ($computer_db->{'pool'}{'domain'} eq $sector) {
2987            my $timestamp = time;
2988            push @{$computer_db->{$sector}}, { $mac => {
2989               'hostname'     => $pool,
2990               'ip'           => $pool,
2991               'address_type' => 'pool-dhcp',
2992               'enabled'      => 'yes',
2993               'create_time'  => $timestamp,
2994               'modify_time'  => $timestamp,
2995               }};
2996            }
2997         else {
2998            print "Ajout de la machine $mac [FAILED]\n";
2999            print "Error: the pool doesn't exists: $pool, for the domain: $sector\n";
3000            }
3001         }
3002      }
3003   }
3004
3005#-------------------------------------------------------------------------------
3006
3007sub load_data_file {
3008   my ($sector, $input_file, $type_file) = @_;
3009
3010   my $computer_db = ipamdb_load($COMPUTER_YAML);
3011
3012   #$computer_db
3013   if ($type_file eq 'dhcp') {
3014      load_data_dhcp($sector, $input_file);
3015      }
3016
3017   elsif ($type_file eq 'pool-dhcp') {
3018      load_data_pool($sector, $input_file);
3019      }
3020
3021   ipamdb_save("$COMPUTER_YAML", $computer_db);
3022   }
3023
3024#-------------------------------------------------------------------------------
3025
3026sub cmd_load_database {
3027   local @ARGV = @_;
3028
3029   my $help = get_cmd_name();
3030   my ($sector, $input_file, $type_file);
3031
3032   GetOptions(
3033      'sector|s|d=s'    => \$sector,
3034      'filename|f=s'    => \$input_file,
3035      'kind|k=s'        => \$type_file,
3036      );
3037
3038   exit_on_error_option($help)
3039      if $sector  eq ''
3040      or $input_file eq ''
3041      or $type_file  eq '';
3042
3043   load_data_file($sector, $input_file, $type_file);
3044   }
3045
3046#-------------------------------------------------------------------------------
3047#Nom: backup_database
3048#Description: sauvegarde et réinitialise les fichiers d'enregistrements DHCP.
3049
3050sub backup_database {
3051   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime time;
3052   $year += 1900;
3053   $mon++;
3054   my $date = sprintf '%04i-%02i-%02i-%02i-%02i-%02i', $year, $mon, $mday, $hour, $min, $sec;
3055
3056   copy($COMPUTER_YAML, "$FOLDER_BACKUP/$COMPUTER_BASENAME-$date.conf") or die "Error: database copy backup failed: $!\n";
3057   }
3058
3059#-------------------------------------------------------------------------------
3060# HELP section
3061#-------------------------------------------------------------------------------
3062
3063#-------------------------------------------------------------------------------
3064#Nom: exit_on_error_option
3065#Description: messages d'aide des options pour les différentes commandes
3066
3067sub exit_on_error_option {
3068  my ($command) = @_;
3069
3070   if ($command eq 'add-dhcp') {
3071      print "List of options for command: $command\n";
3072      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3073      print " -h : computer hostname (mandatory if option -i != 'pool'). Example: -h info8pc154\n";
3074      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A\n";
3075      print " -i : internet IP address (mandatory). Possible value: classical IP address or the keyword 'pool'\n";
3076      print " -p : name of the DHCP pool to which the machine belongs (mandatory if option -i == 'pool')\n";
3077      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
3078      print "Example:\n";
3079      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";
3080      }
3081
3082   elsif ($command eq 'add-float') {
3083      print "List of options for command: $command\n";
3084      print " -s : sector attachment (mandatory)\n";
3085      print " -p : name of the DHCP pool to which the machine belongs (mandatory)\n";
3086      print " -m : physical MAC address (mandatory)\n";
3087      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
3088      print "Example:\n";
3089      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";
3090      }
3091
3092   elsif ($command eq 'add-static') {
3093      print "List of options for command: $command\n";
3094      print " -s : sector attachment (mandatory)\n";
3095      print " -i : internet IP address (mandatory)\n";
3096      print " -h : computer hostname (mandatory)\n";
3097      print " -m : physical MAC address (mandatory)\n";
3098      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
3099      print "Example:\n";
3100      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";
3101      }
3102
3103   elsif ($command eq 'add-virtual') {
3104      print "List of options for command: $command\n";
3105      print " -s : sector attachment (mandatory)\n";
3106      print " -i : internet IP address (mandatory)\n";
3107      print " -h : computer hostname (mandatory)\n";
3108      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
3109      print "Example:\n";
3110      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";
3111      }
3112
3113   elsif ($command eq 'add-alias') {
3114      print "List of options for command: $command\n";
3115      print " -s : sector attachment (mandatory)\n";
3116      print " -h : computer hostname (mandatory)\n";
3117      print " -a : computer alias name (mandatory)\n";
3118      }
3119
3120   elsif ($command eq 'create-sector') {
3121      print "List of options for command: $command\n";
3122      print " -s : new sector (mandatory)\n";
3123      print " -e : DNS domain name extension( mandatory). Example legi.grenoble-inp.fr\n";
3124      print " -c : comment (mandatory). Example: 2016-08-22 VLAN legi-261 (INFO)\n";
3125      print "Examples:\n";
3126      print " ddt create-sector -s legi-sector03 -e legi.grenoble-inp.fr -c '2016-08-22 VLAN legi-261 (INFO)'\n";
3127      }
3128
3129   elsif ($command eq 'create-pool') {
3130      print "List of options for command: $command\n";
3131      print " -p : name of the DHCP pool. Example: pool-legi-priv\n";
3132      print " -s : sector attachment for the pool. (sector attachment must exist in file $COMPUTER_BASENAME.conf). Example: legi-sector03\n";
3133      print " -f : configuration filename on the DHCP server for the pool\n";
3134      print " -i : adresse(s) IP ou plage d'IP. Séparateur d'adresses IP: ','. Séparateur de plage '-'\n";
3135      print "Examples:\n";
3136      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";
3137      print " ddt create-pool -p legi-pool2 -s legi-sector03 -f legi-pool-public  -i 192.168.10.1-192.168.10.4\n";
3138      }
3139
3140   elsif ($command eq 'create-pxe') {
3141      print "List of options for command: $command\n";
3142      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
3143      print " -n : internet IP address for the DHCP next-server.\n";
3144      print " -f : filename on TFTP server to load at boot\n";
3145      print " -c : comment (mandatory). Example: 2014-04-07 PXE Boot for CentOS (MOST)\n";
3146      }
3147
3148   elsif ($command eq 'remove-pxe') {
3149      print "List of options for command: $command\n";
3150      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
3151      }
3152
3153   elsif ($command eq 'enable-pxe') {
3154      print "List of options for command: $command\n";
3155      print " -h : computer hostname (mandatory unless option -i)\n";
3156      print " -i : internet IP address (mandatory unless option -h)\n";
3157      print " -s : sector attachment (mandatory if option -h)\n";
3158      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
3159      }
3160
3161   elsif ($command eq 'disable-pxe') {
3162      print "List of options for command: $command\n";
3163      print " -h : computer hostname (mandatory unless option -i)\n";
3164      print " -i : internet IP address (mandatory unless option -h)\n";
3165      print " -s : sector attachment (mandatory if option -h)\n";
3166      }
3167
3168   elsif ($command eq 'create-tag') {
3169      print "List of options for command: $command\n";
3170      print " -t : name of the TAG (mandatory). Example: restricted\n";
3171      print " -c : comment (mandatory). Example: 2014-04-07 tag restricted (INFO)\n";
3172      print "tag 'universal' is intrinsic\n";
3173      }
3174
3175   elsif ($command eq 'remove-tag') {
3176      print "List of options for command: $command\n";
3177      print " -b : name of the TAG. Example: restricted\n";
3178      }
3179
3180   elsif ($command eq 'change-mac') {
3181      print "List of options for command: $command\n";
3182      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3183      print " -h : computer hostname (mandatory unless option -i)\n";
3184      print " -i : internet IP address (mandatory unless option -h). Possible value: classical IP address or the keyword 'pool'\n";
3185      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
3186      }
3187
3188   elsif ($command eq 'change-ip') {
3189      print "List of options for command: $command\n";
3190      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3191      print " -h : computer hostname (mandatory)\n";
3192      print " -i : new internet IP address (mandatory). Possible value: classical IP address\n";
3193      }
3194
3195   elsif ($command eq 'change-host') {
3196      print "List of options for command: $command\n";
3197      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3198      print " -i : internet IP address (mandatory). Possible value: classical IP address\n";
3199      print " -h : new computer hostname (mandatory)\n";
3200      print "It's not possible to change hostname for computer that belongs to a pool\n";
3201      }
3202
3203   elsif ($command eq 'change-comment') {
3204      print "List of options for command: $command\n";
3205      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3206      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
3207      print " -c : new comment (mandatory)\n";
3208      }
3209
3210   elsif ($command eq 'change-sector') {
3211      print "List of options for command: $command\n";
3212      print " -s : new sector attachment (mandatory). Example: -s legi-sector03\n";
3213      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
3214      print " -i : internet IP address (mandatory)\n";
3215      }
3216
3217   elsif ($command eq 'change-tag') {
3218      print "List of options for command: $command\n";
3219      print " -h : computer hostname (mandatory unless option -i or -m)\n";
3220      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
3221      print " -i : internet IP address (mandatory unless option -h or -m)\n";
3222      print " -m : physical MAC address (mandatory unless option -h or -i, priority). Example: -m 0F:58:AB:2A:22:11\n";
3223      print " -t : list of tags separated by comma (mandatory). Example: -t internal,windows\n";
3224      }
3225
3226   elsif ($command eq 'change-type') {
3227      print "List of options for command: $command\n";
3228      print " -h : computer hostname (mandatory)\n";
3229      print " -s : sector attachment (mandatory)\n";
3230      print " -t : new type). Example: -t static\n";
3231      }
3232
3233   elsif ($command eq 'load-database') {
3234      print "List of options for command: $command\n";
3235      print " -s : sector attachment\n";
3236      print " -f : input file in DHCP format\n";
3237      print " -k : possible cases (kind): dhcp, pool-dhcp, fix-address\n";
3238      }
3239
3240   elsif ($command eq 'enable-pc') {
3241      print "List of options for command: $command\n";
3242      print " -h : computer hostname (mandatory unless option -i)\n";
3243      print " -i : internet IP address (mandatory unless option -h)\n";
3244      print " -s : sector attachment (mandatory if option -h)\n";
3245      print "Examples:\n";
3246      print " ddt enable-pc -i 192.168.10.1\n";
3247      print " ddt enable-pc -s legi-sector03 -h kevinpc\n";
3248      }
3249
3250   elsif ($command eq 'enable-float') {
3251      print "List of options for command: $command\n";
3252      print " -m : physical MAC address (mandatory)\n";
3253      print " -p : name of the DHCP pool (mandatory)\n";
3254      }
3255
3256   elsif ($command eq 'disable-float') {
3257      print "List of options for command: $command\n";
3258      print " -m : physical MAC address (mandatory)\n";
3259      print " -p : name of the DHCP pool (mandatory)\n";
3260      }
3261
3262   elsif ($command eq 'disable-pc') {
3263      print "List of options for command: $command\n";
3264      print " -h : computer hostname (mandatory unless option -i)\n";
3265      print " -i : internet IP address (mandatory unless option -h)\n";
3266      print " -s : sector attachment (mandatory if option -h)\n";
3267      print "Examples:\n";
3268      print " ddt disable-pc -i 192.168.10.1\n";
3269      print " ddt disable-pc -s legi-sector03 -h kevinpc\n";
3270      }
3271
3272   elsif ($command eq 'del-pc') {
3273      print "List of options for command: $command\n";
3274      print " -s : sector attachment (mandatory)\n";
3275      print " -h : computer hostname (mandatory unless option -i)\n";
3276      print " -i : internet IP address (mandatory unless option -h)\n";
3277      }
3278
3279   elsif ($command eq 'del-float') {
3280      print "List of options for command: $command\n";
3281      print " -m : physical MAC address (mandatory)l\n";
3282      print " -p : name of the DHCP pool\n";
3283      }
3284
3285   elsif ($command eq 'search-mac') {
3286      print "List of options for command: $command\n";
3287      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
3288      }
3289
3290   elsif ($command eq 'sector-add-ip') {
3291      print "List of options for command: $command\n";
3292      print " -s : sector attachment (mandatory)\n";
3293      print " -i : internet IP range address in CIDR notation (mandatory)\n";
3294      }
3295
3296   else {
3297      print "No help for command: $command\n";
3298      }
3299   exit;
3300   }
3301
3302#-------------------------------------------------------------------------------
3303
3304sub cmd_version {
3305
3306   print <<'END';
3307ddt - management of computer names and IP addresses
3308Copyright (C) 2006-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
3309Main author Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
3310License GNU GPL version 2 or later and Perl equivalent
3311END
3312
3313   print "Database Version 1\n";
3314   print "Version $VERSION\n\n";
3315   print ' $Id: ddt 364 2018-09-17 07:13:47Z g7moreau $'."\n";
3316   return;
3317   }
3318
3319#-------------------------------------------------------------------------------
3320#Nom: usage
3321#Description: message d'aide sur les commandes du script
3322
3323sub cmd_help {
3324   print <<END;
3325ddt - management of computer names and IP addresses
3326
3327 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]
3328 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3329 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3330 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3331 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]
3332 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3333 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3334 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3335 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3336 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3337 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]
3338 ddt change-type [--hostname|-h hostname] [--sector|-s|-d sector] [--type|-t type]
3339 ddt check-dns [--direct] [--reverse]
3340 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]
3341 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]
3342 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]
3343 ddt create-tag [--tag|-t tag] [--comment|-c comment]
3344 ddt del-float [--pool|-p pool] [--mac|-m mac]
3345 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3346 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3347 ddt disable-float [--pool|-p pool] [--mac|-m mac]
3348 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3349 ddt enable-float [--pool|-p pool] [--mac|-m mac]
3350 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3351 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]
3352 ddt gen-dhcp-file
3353 ddt gen-dns-file [--verbose]
3354 ddt help
3355 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]
3356 ddt remove-pxe [--bootp|-b pxe_config]
3357 ddt remove-tag [--tag|-t tag]
3358 ddt search-mac [--mac|-m mac]
3359 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr]
3360 ddt show-sector [--no-header|-H]
3361 ddt show
3362 ddt show-pool [--no-header|-H]
3363 ddt show-pxe [--no-header|-H]
3364 ddt show-tag [--no-header|-H]
3365 ddt version
3366
3367COMMANDS
3368
3369 * add-alias         : add an alias for a computer (like CNAME for the DNS)
3370 * add-dhcp          : add a computer with a fix DHCP IP or in a DHCP pool
3371 * add-float         : add a computer with an IP in a DHCP pool
3372 * add-static        : add a computer with a static IP
3373 * add-virtual       : add a virtual computer with a static IP but a virtual MAC (useful to declare float computer in DNS)
3374 * change-comment    : change the computer comment
3375 * change-sector     : change the sector attachment for a computer
3376 * change-host       : change the computer hostname
3377 * change-ip         : change the computer IP address
3378 * change-mac        : change the computer physical MAC address
3379 * change-tag        : change the list of TAGs associated to a computer
3380 * change-type       : change the type associated to a computer
3381 * check-dns         : check the DNS table for base IPs
3382 * create-sector     : create a new sector
3383 * create-pool       : create a new pool for DHCP records
3384 * create-pxe        : create a new PXE/BOOTP configuration
3385 * create-tag        : create a new TAG
3386 * del-float         : remove a computer from a DHCP pool
3387 * del-pc            : remove a computer (DHCP or static IP) from the YAML database
3388 * disable-pc        : disable a computer (DHCP and/or DNS) (but keep it in the database)
3389 * disable-float     : disable a computer from a DHCP pool (but keep it in the database)
3390 * disable-pxe       : remove PXE/BOOTP configuration on a computer
3391 * enable-float      : enable a previous disable computer (DHCP and/or DNS)
3392 * enable-pc         : enable a previous disable computer (DHCP and/or DNS)
3393 * enable-pxe        : enable PXE/BOOTP configuration on a computer
3394 * gen-dhcp-file     : generate DHCP files for the isc DHCP server
3395 * gen-dns-file      : generate DNS files for the bind domain server
3396 * help              : this help
3397 * load-database     : load the YAML database (be careful)
3398 * remove-pxe        : remove a PXE/BOOTP configuration
3399 * remove-tag        : remove a TAG
3400 * search-mac        : search physical MAC address computer
3401 * ddt sector-add-ip : add IP range check on a sector
3402 * show-sector       : list all sector group of computer
3403 * show              : list all computers
3404 * show-pool         : list all pool
3405 * show-pxe          : list PXE/BOOTP configuration
3406 * show-tag          : list all TAGs
3407 * version           : return program version
3408END
3409   return;
3410   }
3411
3412################################################################
3413# documentation
3414################################################################
3415
3416__END__
3417
3418=head1 NAME
3419
3420ddt - management of computer names and IP addresses
3421
3422
3423=head1 USAGE
3424
3425 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]
3426 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3427 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3428 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3429 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]
3430 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3431 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3432 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3433 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3434 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3435 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]
3436 ddt change-type [--hostname|-h hostname] [--sector|-s|-d sector] [--type|-t type]
3437 ddt check-dns [--direct] [--reverse]
3438 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]
3439 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]
3440 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]
3441 ddt create-tag [--tag|-t tag] [--comment|-c comment]
3442 ddt del-float [--pool|-p pool] [--mac|-m mac]
3443 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3444 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3445 ddt disable-float [--pool|-p pool] [--mac|-m mac]
3446 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3447 ddt enable-float [--pool|-p pool] [--mac|-m mac]
3448 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3449 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]
3450 ddt gen-dhcp-file
3451 ddt gen-dns-file [--verbose]
3452 ddt help
3453 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]
3454 ddt remove-pxe [--bootp|-b pxe_config]
3455 ddt remove-tag [--tag|-t tag]
3456 ddt search-mac [--mac|-m mac]
3457 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr]
3458 ddt show-sector [--no-header|-H]
3459 ddt show
3460 ddt show-pool [--no-header|-H]
3461 ddt show-pxe [--no-header|-H]
3462 ddt show-tag [--no-header|-H]
3463 ddt version
3464
3465
3466=head1 DESCRIPTION
3467
3468DDT is an acronym for DHCP-DNS-Tools.
3469The previous command name was not C<ddt> but just C<dhcp-dns-tools>...
3470In practise, DDT is an IP Address Management (IPAM) service.
3471It has been used in the LEGI laboratory for over 10 years.
3472
3473The tool is quite effective and tries to keep things simple
3474but easily configurable for your site like a swiss army knife.
3475Everything is saved in a YAML database
3476and entries could be added, deleted, or modified by the command line.
3477
3478
3479=head1 COMMANDS
3480
3481=head2 add-alias
3482
3483 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]
3484
3485=head2 add-dhcp
3486
3487 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3488
3489=head2 add-float
3490
3491 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3492
3493=head2 add-static
3494
3495 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
3496
3497=head2 add-virtual
3498
3499 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]
3500
3501=head2 change-comment
3502
3503 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
3504
3505=head2 change-sector
3506
3507 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3508
3509=head2 change-host
3510
3511 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3512
3513=head2 change-ip
3514
3515 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3516
3517=head2 change-mac
3518
3519 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
3520
3521=head2 change-tag
3522
3523 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]
3524
3525=head2 change-type
3526
3527 ddt change-type [--hostname|-h hostname] [--sector|-s|-d sector] [--type|-t type]
3528
3529=head2 check-dns
3530
3531 ddt check-dns [--direct] [--reverse]
3532
3533=head2 create-sector
3534
3535 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]
3536
3537=head2 create-pool
3538
3539 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]
3540
3541=head2 create-pxe
3542
3543 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]
3544
3545=head2 create-tag
3546
3547 ddt create-tag [--tag|-t tag] [--comment|-c comment]
3548
3549=head2 del-float
3550
3551 ddt del-float [--pool|-p pool] [--mac|-m mac]
3552
3553=head2 del-pc
3554
3555 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3556
3557=head2 disable-pc
3558
3559 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3560
3561=head2 disable-float
3562
3563 ddt disable-float [--pool|-p pool] [--mac|-m mac]
3564
3565=head2 disable-pxe
3566
3567 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3568
3569=head2 enable-float
3570
3571 ddt enable-float [--pool|-p pool] [--mac|-m mac]
3572
3573=head2 enable-pc
3574
3575 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
3576
3577=head2 enable-pxe
3578
3579 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]
3580
3581=head2 gen-dhcp-file
3582
3583 ddt gen-dhcp-file
3584
3585=head2 gen-dns-file
3586
3587 ddt gen-dns-file [--verbose]
3588
3589=head2 help
3590
3591 ddt help
3592
3593=head2 load-database
3594
3595 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]
3596
3597=head2 remove-pxe
3598
3599 ddt remove-pxe [--bootp|-b pxe_config]
3600
3601=head2 remove-tag
3602
3603 ddt remove-tag [--tag|-t tag]
3604
3605=head2 search-mac
3606
3607 ddt search-mac [--mac|-m mac]
3608
3609=head2 sector-add-ip
3610
3611 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr]
3612
3613=head2 show-sector
3614
3615 ddt show-sector [--no-header|-H]
3616
3617=head2 show
3618
3619 ddt show
3620
3621=head2 show-pool
3622
3623 ddt show-pool [--no-header|-H]
3624
3625=head2 show-pxe
3626
3627 ddt show-pxe [--no-header|-H]
3628
3629=head2 show-tag
3630
3631 ddt show-tag [--no-header|-H]
3632
3633=head2 version
3634
3635 ddt version
3636
3637
3638=head1 AUTHORS
3639
3640Written by Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>, Kevin Reverchon, Olivier De-Marchi - Grenoble - France
3641
3642
3643=head1 LICENSE AND COPYRIGHT
3644
3645License GNU GPL version 2 or later and Perl equivalent
3646
3647Copyright (C) 2006-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
Note: See TracBrowser for help on using the repository browser.