source: trunk/ddt/ddt @ 368

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