#!/usr/bin/perl
#
# Copyright (C) 2006-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
# License GNU GPL version 2 or later and Perl equivalent
#
# apt-get install perl-base perl-modules libyaml-syck-perl libnet-netmask-perl libreadonly-perl libfile-touch-perl libtext-table-perl libnetaddr-ip-perl

package DDT::RE;

use strict;
#use warnings;

use Readonly;

Readonly our $MAC_ADDRESS  => qr{ (?: [0-9A-F]{2} :){5} [0-9A-F]{2} }xms;
Readonly our $IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;


package main;

use strict;
#use warnings;
use version; our $VERSION = version->declare('0.11.7');

use Getopt::Long qw(GetOptions);
#use YAML;
use YAML::Syck;
use Net::Netmask;
use File::Touch;
use File::Copy;
use Socket;
use Text::Table;
use NetAddr::IP;

my $command = shift @ARGV || 'help';

my %cmd_db = (
   'add-alias'          => \&cmd_add_alias,
   'add-dhcp'           => \&cmd_add_dhcp,
   'add-float'          => \&cmd_add_float,
   'add-static'         => \&cmd_add_static,
   'add-virtual'        => \&cmd_add_virtual,
   'change-comment'     => \&cmd_change_comment,
   'change-sector'      => \&cmd_change_sector,
   'change-host'        => \&cmd_change_host,
   'change-ip'          => \&cmd_change_ip,
   'change-mac'         => \&cmd_change_mac,
   'change-tag'         => \&cmd_change_tag,
   'check-dns'          => \&cmd_check_dns,
   'create-sector'      => \&cmd_create_sector,
   'create-pool'        => \&cmd_create_pool,
   'create-pxe'         => \&cmd_create_pxe,
   'create-tag'         => \&cmd_create_tag,
   'del-pc'             => \&cmd_del_pc,
   'del-float'          => \&cmd_del_float,
   'disable-pc'         => \&cmd_disable_pc,
   'disable-float'      => \&cmd_disable_float,
   'disable-pxe'        => \&cmd_disable_pxe,
   'enable-pc'          => \&cmd_enable_pc,
   'enable-float'       => \&cmd_enable_float,
   'enable-pxe'         => \&cmd_enable_pxe,
   'gen-dhcp-file'      => \&cmd_generate_dhcp_file,
   'gen-dns-file'       => \&cmd_generate_dns_file,
   'help'               => \&cmd_help,
   'load-database'      => \&cmd_load_database,
   'remove-pxe'         => \&cmd_remove_pxe,
   'remove-tag'         => \&cmd_remove_tag,
   'search-mac'         => \&cmd_search_mac,
   'sector-add-ip'      => \&cmd_sector_add_ip,
   'show'               => \&cmd_show_host,
   'show-sector'        => \&cmd_show_sector,
   'show-pool'          => \&cmd_show_pool,
   'show-pxe'           => \&cmd_show_pxe,
   'show-tag'           => \&cmd_show_tag,
   'upgrade-db'         => \&cmd_upgrade_db,
   'version'            => \&cmd_version,
   );

#-------------------------------------------------------------------------------

my $CONFIG;

my $xdg_config_home = $ENV{'XDG_CONFIG_HOME'} || "$ENV{'HOME'}/.config";
$CONFIG = config_load("$xdg_config_home/ddt/config.yml") if -e "$xdg_config_home/ddt/config.yml";

my $COMPUTER_BASENAME   = $CONFIG->{'database'}{'basename'} || 'ddt';
my $COMPUTER_EXT        = $CONFIG->{'database'}{'ext'}      || 'db';

my $FOLDER_APP          = $CONFIG->{'database'}{'folder'}   || '/var/lib/ddt';
my $FOLDER_BACKUP       = $CONFIG->{'database'}{'backup'}   || "$FOLDER_APP/backup";
my $FOLDER_GEN_DHCP     = $CONFIG->{'generate'}{'dhcp'}     || "$FOLDER_APP/dhcp";
my $FOLDER_GEN_DNS      = $CONFIG->{'generate'}{'dns'}      || "$FOLDER_APP/dns";
my $SCRIPT_UPDATE       = $CONFIG->{'script'}{'update'}     || '/usr/share/ddt/update-dhcp-server';

my $COMPUTER_YAML       = "$FOLDER_APP/$COMPUTER_BASENAME.$COMPUTER_EXT";

#-------------------------------------------------------------------------------

mkdir $FOLDER_APP, 0755      if not -d $FOLDER_APP;
mkdir $FOLDER_BACKUP, 0755   if not -d $FOLDER_BACKUP;
mkdir $FOLDER_GEN_DHCP, 0755 if not -d $FOLDER_GEN_DHCP;
mkdir $FOLDER_GEN_DNS, 0755  if not -d $FOLDER_GEN_DNS;

if (defined $cmd_db{$command}) {
   $cmd_db{$command}->(@ARGV);
   }
else {
   print {*STDERR} "ddt: command $command not found\n\n";
   $cmd_db{'help'}->();
   exit 1;
   }

exit;

#--------------------------------------------------------------------------------
# LOAD SAVE section
#--------------------------------------------------------------------------------

sub config_load {
   my $config_file = shift;

   my $configdb = YAML::Syck::LoadFile($config_file);

   return $configdb;
   }

#---------------------------------------------------------------
# Load computer database

sub ipamdb_load {
   my $database_yaml = shift;

   touch $database_yaml if not -e $database_yaml;
   my $computer_db = YAML::Syck::LoadFile($database_yaml);

   # add database version if not exist
   if (not exists $computer_db->{'version'}) {
      $computer_db->{'version'} = 1;
      }

   return $computer_db;
   }

#---------------------------------------------------------------
# Save computer database

sub ipamdb_save {
   my ($database_yaml, $computer_db) = @_;

   my $dirdb = $database_yaml;
      $dirdb =~ s{ / [^/]* $}{}xms;
   mkdir "$dirdb", 0755 unless -d "$dirdb";
   YAML::Syck::DumpFile($database_yaml, $computer_db);

   return $computer_db;
   }

#--------------------------------------------------------------------------------
# CONTROL section
#--------------------------------------------------------------------------------

sub control_exist_pool {
   my ($computer_db, $pool) = @_;

   return exists $computer_db->{'pool'}{$pool} ? 1 : 0;
   }

#-------------------------------------------------------------------------------
#Nom: control_exist_sector
#Description: controle l'existence d'un sector dans le fichier YAML
#             return 0 (faux) ou 1 (vrai)

sub control_exist_sector {
   my ($computer_db, $sector) = @_;

   return 1 if exists $computer_db->{$sector};

   print {*STDERR} "Error: sector $sector not found\n";
   return 0;
   }

#-------------------------------------------------------------------------------
#Nom: control_exist_hostname
#Description: controle l'existence d'un nom de machine dans le fichier YAML
#             return 0 (si trouvé) ou 1 (si non trouvé)

sub control_exist_hostname {
   my ($computer_db, $sector, $hostname) = @_;

   if ($computer_db->{$sector} eq '') {
      return 1;
      }

   my @sectordb = @{$computer_db->{$sector}};

   for my $computer (@sectordb) {
      my ($mac_address, $attribute) = %{$computer};
      return 0 if $attribute->{'hostname'} eq $hostname;
      }
   return 1;
   }

#-------------------------------------------------------------------------------
#Nom: control_exist_mac
#Description: controle l'existence d'une adresse MAC dans le fichier YAML
#             return 0 (si trouvé) ou 1 (si non trouvé)

sub control_exist_mac {
   my ($computer_db, $mac) = @_;

   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      my @sectordb = @{$computer_db->{$sector_current}};

      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};
         return 0 if $mac_address eq $mac;
         }
      }
   return 1;
   }

#-------------------------------------------------------------------------------
#Nom: control_exist_ip
#Description: controle l'existence d'une adresse IP dans le fichier YAML
#             return 0 (si trouvé) ou 1 (si non trouvé)

sub control_exist_ip {
   my ($computer_db, $ip) = @_;

   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      LOOP_ON_COMPUTER:
      for my $computer (@{$computer_db->{$sector_current}}) {
         my ($mac_address, $attribute) = %{$computer};
         #print "Erreur: cette adresse IP $ip existe déjà\n";
         return 0 if $attribute->{'ip'} eq $ip;
         }
      }

   for my $current_pool (keys %{$computer_db->{'pool'}}) {
      #--- Cette partie pour tester les ip des pools est bonne ne plus la changer ---#
      my @T_pool_ip = @{$computer_db->{'pool'}{$current_pool}{'ip'}};

      for my $pool_ip (@T_pool_ip) {
         #print "Erreur: cette adresse IP $ip existe déjà\n";
         return 0 if $pool_ip eq $ip;
         }
      }

   return 1;
   }

#-------------------------------------------------------------------------------

sub control_ip_in_range {
   my ($computer_db, $sector, $ip) = @_;

   return 1 if not exists $computer_db->{'dset'}{$sector}{'ip_range'}; # No IP range defined for this sector

   my $ip_addr = NetAddr::IP->new($ip);

   LOOP_ON_IP_RANGE:
   for my $ip_range_current (@{$computer_db->{'dset'}{$sector}{'ip_range'}}) {
      my $range = NetAddr::IP->new($ip_range_current);
      return 1 if $range->contains($ip_addr);
      }

   return 0;
   }

#-------------------------------------------------------------------------------------
#Nom: control_syntaxe_mac
#Description: controle la syntaxe d'une adresse MAC (juste la longueur pas les valeurs)
#             return 0 (si trouvé) ou 1 (si non trouvé)

sub control_syntax_mac_address {
   my $mac = shift;

   if (scalar(split /:/, $mac) == 6 and $mac =~ $DDT::RE::MAC_ADDRESS) {
      return 1;
      }

   print {*STDERR} "Error: bad MAC syntax: $mac\n";
   return 0;
   }

#-------------------------------------------------------------------------------------
#Nom: control_syntax_ip
#Description: controle la syntaxe d'une adresse IP (juste la longueur pas les valeurs)
#             return 0 (si trouvé) ou 1 (si non trouvé)

sub control_syntax_ip {
   my $ip = shift;

   return 1 if $ip ne 'pool';

   return 0 if $ip !~ m{^(\d+\.){3}\d+$};
   return 0 if not NetAddr::IP->new("$ip/32");
   return 1;
   }

#-------------------------------------------------------------------------------------

sub control_syntax_cidr {
   my $cidr = shift;

   return 0 if $cidr !~ m{^(\d+\.){3}\d+/\d+$};
   return 0 if not NetAddr::IP->new($cidr);
   return 1;
   }

#-------------------------------------------------------------------------------------

sub control_syntax_comment {
   my $comment = shift;

   if ($comment !~ m{^20\d\d-\d\d-\d\d\s}) {
      print {*STDERR} "Error: no date like 2014-01-10 at the beginning: $comment\n";
      return 0;
      }

   if ($comment !~ m{\(\w+\)$}) {
      print {*STDERR} "Error: no (SERVICE) at the end: $comment\n";
      return 0;
      }

   if ($comment =~ m{\s\s}) {
      print {*STDERR} "Error: double space: $comment\n";
      return 0;
      }
   return 1;
   }

#--------------------------------------------------------------------------------
# UTILITY section
#--------------------------------------------------------------------------------

sub get_cmd_name {
   my ($pkg, $sub) = split /::/, (caller(1))[3];
   $sub =~ s/^cmd_//;
   $sub =~ s/_/-/g;
   return $sub;
   }

#-------------------------------------------------------------------------------

sub normalize_mac_address {
   my $mac_address = shift;

   # D07E-28D1-7AB8 or d07e28-d17ab8
   if ($mac_address =~ m{^ (?: [0-9A-Fa-f]{4} -){2} [0-9A-Fa-f]{4} $}xms
      or $mac_address =~ m{^ [0-9A-Fa-f]{6} - [0-9A-Fa-f]{6} $}xms) {
      $mac_address =~ s/-//g;
      return join q{:}, unpack('(A2)*', uc($mac_address));
      }

   return join q{:}, map { substr( uc("00$_"), -2) } split m/ [:-] /xms, $mac_address;
   }

#-------------------------------------------------------------------------------

sub normalize_comment {
   my $comment = shift;

   $comment =~ s{^(20\d\d)/(\d\d)/(\d\d)\s(.*)$}{$1-$2-$3 $4};

   return $comment;
   }

#--------------------------------------------------------------------------------

sub get_mac_from_hostname {
   my ($computer_db, $sector, $hostname, $mac) = @_;

   return $mac if $mac ne '';
   return ''   if $hostname eq '';

   LOOP_ON_COMPUTER:
   for my $computer (@{$computer_db->{$sector}}) {
      my ($mac_address, $attribute) = %{$computer};

      next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;

      return $mac_address;
      }
   }

#--------------------------------------------------------------------------------

sub get_mac_from_ip {
   my ($computer_db, $sector, $ip, $mac) = @_;

   return $mac if $mac ne '';
   return ''   if $ip eq '';

   LOOP_ON_COMPUTER:
   for my $computer (@{$computer_db->{$sector}}) {
      my ($mac_address, $attribute) = %{$computer};

      next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
      return $mac_address;
      }
   }

#--------------------------------------------------------------------------------
# return a tuple (hash computer, iostat)
# iostat 0/ok, 1/not exist

sub get_computer_from_mac {
   my ($computer_db, $sector, $mac) = @_;

   LOOP_ON_COMPUTER:
   for my $computer (@{$computer_db->{$sector}}) {
      my ($mac_address, $attribute) = %{$computer};

      next LOOP_ON_COMPUTER if $mac_address ne $mac;

      return $attribute, 0;
      }
   return {}, 1;
   }

#-------------------------------------------------------------------------------
# ADD computer section
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#Nom: add_alias
#Description: ajoute un alias pour une machine. Pour la fonctionnalité CNAME dans le DNS.

sub add_alias {
   my ($computer_db, $hostname, $sector, $alias) = @_;

   control_exist_sector($computer_db, $sector) or exit;
   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";

   my @sectordb = @{$computer_db->{$sector}};

   LOOP_ON_COMPUTER:
   for my $computer (@sectordb) {
      my ($mac_address, $attribute) = %{$computer};

      next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;

      $alias .= ' ' . $attribute->{'alias'};
      $attribute->{'alias'}       = $alias;
      $attribute->{'modify_time'} = time;
      ipamdb_save("$COMPUTER_YAML", $computer_db);
      print "Info: update attribute alias to $alias for host $hostname [OK]\n";
      exit;
      }
   }

#-------------------------------------------------------------------------------
#Nom: add_static
#Description: ajoute une machine non dhcp (donc à adressage fixe dans le fichier YAML)

sub add_static {
   my ($computer_db, $hostname, $sector, $ip, $mac, $comment) = @_;

   $mac = normalize_mac_address($mac);
   $comment = normalize_comment($comment);
   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";
   control_syntax_mac_address($mac)                         or exit;
   control_exist_mac($computer_db, $mac)                    or die "Error: physical MAC address already exists: $mac\n";
   control_syntax_ip($ip)                                   or die "Error: bad IP syntax $ip\n";
   control_exist_ip($computer_db, $ip)                      or die "Error: IP $ip address already exist in sector $sector\n";
   control_ip_in_range($computer_db, $sector, $ip)          or die "Error: IP $ip is not in sector $sector IP range.\n";
   control_syntax_comment($comment)                         or exit;
   my $timestamp = time;
   push @{$computer_db->{$sector}}, { $mac => {
      'hostname'     => $hostname,
      'ip'           => $ip,
      'address_type' => 'static',
      'enabled'      => 'yes',
      'create_time'  => $timestamp,
      'modify_time'  => $timestamp,
      'comment'      => $comment,
      'alias'        =>  '',
      }};
   print "Info: add the host: $hostname, IP: $ip, MAC: $mac, sector: $sector [OK]\n";

   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }


#-------------------------------------------------------------------------------
#Nom: add_dhcp
#Description: section à corriger pour prendre en compte l'ajout d'une machine dans un pool dhcp
#--- usage: ddt add-dhcp -s legi-sector03 -h meolpacif -m 00:18:F3:03:6F:66 -i 194.254.66.165

sub add_dhcp {
   my ($computer_db, $hostname, $sector, $ip, $mac, $comment) = @_;

   my $timestamp = time;
   $mac = normalize_mac_address($mac);
   $comment = normalize_comment($comment);
   control_exist_sector($computer_db, $sector)              or exit;
   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";
   control_syntax_mac_address($mac)                         or exit;
   control_exist_mac($computer_db, $mac)                    or die "Error: physical MAC address already exists: $mac\n";
   control_syntax_ip($ip)                                   or die "Error: bad IP syntax $ip\n";
   control_exist_ip($computer_db, $ip)                      or die "Error: IP address already exist in sector $sector: $ip.\n";
   control_ip_in_range($computer_db, $sector, $ip)          or die "Error: IP $ip is not in sector $sector IP range.\n";
   control_syntax_comment($comment)                         or exit;

   push @{$computer_db->{$sector}}, { $mac => {
      'hostname'     => $hostname,
      'ip'           => $ip,
      'address_type' => 'dhcp',
      'enabled'      => 'yes',
      'create_time'  => $timestamp,
      'modify_time'  => $timestamp,
      'comment'      => $comment,
      'alias'        => '',
      }};
   print "Add the computer: $hostname, IP: $ip, MAC: $mac, sector: $sector\n";

   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------
#--- usage: ddt add-float -s legi-sector03 -h meolpacif -m 00:18:F3:03:6F:66 -i 194.254.66.165

sub add_float {
   my ($computer_db, $pool, $sector, $mac, $comment) = @_;

   my $timestamp = time;
   $mac = normalize_mac_address($mac);
   $comment = normalize_comment($comment);
   control_exist_sector($computer_db, $sector)  or exit;
   control_syntax_mac_address($mac)                   or exit;
   control_exist_mac($computer_db, $mac)              or die "Error: physical MAC address already exists: $mac\n";
   control_exist_pool($computer_db, $pool)            or die "Error: the pool doesn't exists: $pool\n";
   control_syntax_comment($comment)                   or exit;
   push @{$computer_db->{$sector}}, { $mac => {
      'hostname'     => $pool,
      'ip'           => $pool,
      'address_type' => 'pool-dhcp',
      'enabled'      => 'yes',
      'create_time'  => $timestamp,
      'modify_time'  => $timestamp,
      'comment'      => $comment,
      }};
   print "Info: add the computer in pool MAC: $mac, sector: $sector, Pool: $pool [OK]\n";

   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------
# ADD computer section
#-------------------------------------------------------------------------------

sub cmd_add_alias {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $alias);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'alias|a=s'       => \$alias,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $hostname   eq ''
      or $sector  eq ''
      or $alias      eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);
   add_alias($computer_db, $hostname, $sector, $alias);
   }

#-------------------------------------------------------------------------------

sub cmd_add_dhcp {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip, $mac, $comment);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      'mac|m=s'         => \$mac,
      'comment|c=s'     => \$comment,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $hostname   eq ''
      or $sector  eq ''
      or $ip         eq ''
      or $mac        eq ''
      or $comment    eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);
   add_dhcp($computer_db, $hostname, $sector, $ip, $mac, $comment);
   }

#-------------------------------------------------------------------------------

sub cmd_add_float {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($pool, $sector, $mac, $comment);

   GetOptions(
      'pool|p=s'        => \$pool,
      'sector|s|d=s'    => \$sector,
      'mac|m=s'         => \$mac,
      'comment|c=s'     => \$comment,
      );

   ($pool, $sector) = split /\./, $pool, 2 if $pool =~ m/\./;
   exit_on_error_option($help)
      if $pool       eq ''
      or $sector  eq ''
      or $mac        eq ''
      or $comment    eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);
   add_float($computer_db, $pool, $sector, $mac, $comment);
   }

#-------------------------------------------------------------------------------

sub cmd_add_static {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip, $mac, $comment);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      'mac|m=s'         => \$mac,
      'comment|c=s'     => \$comment,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $hostname   eq ''
      or $sector  eq ''
      or $ip         eq ''
      or $mac        eq ''
      or $comment    eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);
   add_static($computer_db, $hostname, $sector, $ip, $mac, $comment);
   }

#-------------------------------------------------------------------------------
# No real computer, just an entry A in DNS with virtual MAC

sub cmd_add_virtual {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip, $comment);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      'comment|c=s'     => \$comment,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $hostname   eq ''
      or $sector  eq ''
      or $ip         eq ''
      or $comment    eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   $comment = normalize_comment($comment);
   my $timestamp = time;

   control_exist_sector($computer_db, $sector)              or exit;
   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";
   control_syntax_ip($ip)                                   or die "Error: bad IP syntax $ip\n";
   control_exist_ip($computer_db, $ip)                      or die "Error: IP address already exist in sector $sector: $ip.\n";
   control_ip_in_range($computer_db, $sector, $ip)          or die "Error: IP $ip is not in sector $sector IP range.\n";
   control_syntax_comment($comment)                         or exit;

   my $mac = join ':', 'FF', 'FF', map({sprintf("%02X", $_)} split(/\./, $ip));
   control_syntax_mac_address($mac)             or exit;
   control_exist_mac($computer_db, $mac)         or die "Error: virtual physical MAC address already exists: $mac\n";

   push @{$computer_db->{$sector}}, { $mac => {
      'hostname'     => $hostname,
      'ip'           => $ip,
      'address_type' => 'static',
      'enabled'      => 'yes',
      'create_time'  => $timestamp,
      'modify_time'  => $timestamp,
      'comment'      => $comment,
      }};
   print "Add the virtual computer: $hostname, IP: $ip, sector: $sector\n";

   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------
# CHANGE computer section
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#Nom: change_mac
#Description: change la mac adresse d'une machine en saisissant soit l'ip
#             soit le nom de la mahcine et spécifiant le domaine
#--- usage: ddt change-mac -s legi-sector03 -h meolpacif -m 00:18:F3:03:6F:66
#--- usage: ddt change-mac -s legi-sector03 -i 194.254.66.187 -m 00:18:F3:03:6F:66

sub change_mac {
   my ($hostname, $sector, $ip, $mac) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   $mac = normalize_mac_address($mac);
   control_exist_sector($computer_db, $sector)  or exit;
   control_syntax_mac_address($mac)                   or exit;
   control_exist_mac($computer_db, $mac)              or die "Error: physical MAC address already exists: $mac\n";
   if ($ip ne '') {
      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";
      if ( control_exist_ip($computer_db, $ip) == 1 ) {
         print "Error: unkown IP address: $ip\n";
         exit;
         }
      my @sectordb = @{$computer_db->{$sector}};
      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};
         die "Error: physical MAC address $mac already exists in sector $sector\n" if $mac_address eq $mac;

         next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;

         $attribute->{'modify_time'} = time;
         $computer->{$mac} = $attribute;     # add new mac
         delete $computer->{$mac_address};   # remove old mac

         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: update host $attribute->{'hostname'}, sector $sector, MAC $mac, IP $attribute->{'ip'} [OK]\n";
         exit;
         }
      }
   elsif ($hostname ne '') {
      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
         die "Error: unkown host $hostname, sector $sector\n";
         }
      my @sectordb = @{$computer_db->{$sector}};
      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};
         die "Error: physical MAC address $mac already exists in sector $sector\n" if $mac_address eq $mac;

         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;

         $attribute->{'modify_time'} = time;
         $computer->{$mac} = $attribute;     # add new mac
         delete $computer->{$mac_address};   # remove old mac

         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: update host $attribute->{'hostname'}, sector $sector, MAC $mac, IP $attribute->{'ip'} [OK]\n";
         exit;
         }
      }
   }

#-------------------------------------------------------------------------------
#Nom: change_ip
#Description: change l'adresse IP d'une machine en saisissant le nom de la machine
#             et le domaine

sub change_ip {
   my ($hostname, $sector, $ip) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   control_exist_sector($computer_db, $sector) or exit;
   if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
      die "Error: unkown host: $hostname, in sector: $sector\n";
      }
   control_syntax_ip($ip)                          or die "Error: bad IP syntax $ip\n";
   control_exist_ip($computer_db, $ip)             or die "Error: IP $ip address already exist in sector $sector\n";
   control_ip_in_range($computer_db, $sector, $ip) or die "Error: IP $ip is not in sector $sector IP range.\n";

   my @sectordb = @{$computer_db->{$sector}};

   LOOP_ON_COMPUTER:
   for my $computer (@sectordb) {
      my ($mac_address, $attribute) = %{$computer};
      
      next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
 
      if ($attribute->{'address_type'} eq 'pool-dhcp') {
         die "Error: host $hostname from sector $sector belongs to a a pool [FAILED]" .
            " ... use 'del-float' command before";
         }

      $attribute->{'modify_time'} = time;
      $attribute->{'ip'}          = $ip;
      ipamdb_save("$COMPUTER_YAML", $computer_db);
      print "Info: update host $hostname MAC: $mac_address IP: $ip [OK]\n";
      exit;
      }
   }

#-------------------------------------------------------------------------------
#Nom: change_host
#Description: change le computer hostname en saisissant l'IP et le domaine

sub change_host {
   my ($hostname, $sector, $ip) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   control_exist_sector($computer_db, $sector) or exit;
   control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";
   if ( control_exist_ip($computer_db, $ip) == 1 ) {
      die "Error: unkown IP address: $ip\n";
      }
   control_exist_hostname($computer_db, $sector, $hostname) or die "Error: host already exist in sector $sector: $hostname\n";

   my @sectordb = @{$computer_db->{$sector}};

   LOOP_ON_COMPUTER:
   for my $computer (@sectordb) {
      my ($mac_address, $attribute) = %{$computer};

      next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;

      $attribute->{'modify_time'} = time;
      $attribute->{'hostname'}    = $hostname;
      ipamdb_save("$COMPUTER_YAML", $computer_db);
      print "Info: update host $hostname MAC: $mac_address IP: $ip [OK]\n";
      exit;
      }

   die "Error: failed to update hostname $hostname [FAILED]\n" .
      " ... no IP $ip belongs to the sector $sector\n";
   }

#--------------------------------------------------------------------------------

sub cmd_change_mac {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip, $mac);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      'mac|m=s'         => \$mac,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $sector  eq ''
      or $mac        eq '';
   exit_on_error_option($help)
      if $hostname   ne ''
      and $ip        ne '';

   change_mac($hostname, $sector, $ip, $mac);
   }

#--------------------------------------------------------------------------------

sub cmd_change_ip {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $hostname   eq ''
      or $sector  eq ''
      or $ip         eq '';

   change_ip($hostname, $sector, $ip);
   }

#--------------------------------------------------------------------------------

sub cmd_change_host {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $hostname   eq ''
      or $sector  eq ''
      or $ip         eq '';

   change_host($hostname, $sector, $ip);
   }

#--------------------------------------------------------------------------------

sub cmd_change_comment {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($sector, $mac, $comment);

   GetOptions(
      'sector|s|d=s'    => \$sector,
      'mac|m=s'         => \$mac,
      'comment|c=s'     => \$comment,
      );

   exit_on_error_option($help)
      if $sector  eq ''
      or $mac        eq ''
      or $comment    eq '';

   $mac     = normalize_mac_address($mac);
   $comment = normalize_comment($comment);

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   control_exist_sector($computer_db, $sector)  or exit;
   control_syntax_mac_address($mac)                   or exit;
   control_syntax_comment($comment)                   or exit;

   my @sectordb = @{$computer_db->{$sector}};

   LOOP_ON_COMPUTER:
   for my $computer (@sectordb) {
      my ($mac_address, $attribute) = %{$computer};

      next LOOP_ON_COMPUTER if $mac_address ne $mac;

      $attribute->{'modify_time'} = time;
      $attribute->{'comment'}     = $comment;
      ipamdb_save("$COMPUTER_YAML", $computer_db);
      exit;
      }
   die "Error : Host $mac comment [FAILED]\n" .
      " ... No MAC: $mac belongs to the domaine set $sector.\n";
   }

#--------------------------------------------------------------------------------

sub cmd_change_sector {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($sector, $ip, $mac);

   GetOptions(
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      'mac|m=s'         => \$mac,
      );

   exit_on_error_option($help)
      if $sector  eq ''
      or $ip         eq ''
      or $mac        eq '';

   $mac = normalize_mac_address($mac);

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   control_exist_sector($computer_db, $sector)  or exit;
   control_syntax_ip($ip)                       or die "Error: bad IP syntax $ip\n";
   control_syntax_mac_address($mac)             or exit;

   LOOP_ON_SECTOR:
   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      my @sectordb = @{$computer_db->{$sector_current}};
      my $computer_index = 0;
      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};

         $computer_index++, next LOOP_ON_COMPUTER if $mac_address ne $mac;
         next LOOP_ON_SECTOR if $attribute->{'ip'} ne $ip;

         $attribute->{'modify_time'} = time;
         splice(@{$computer_db->{$sector_current}}, $computer_index => 1);
         push @{$computer_db->{$sector}}, { $mac => $attribute };

         ipamdb_save("$COMPUTER_YAML", $computer_db);
         exit;
         }
      }
   die "Error: update of sector $sector [FAILED]\n" .
      " ... MAC $mac and IP $ip don't exists in the database\n";
   }

#--------------------------------------------------------------------------------

sub cmd_change_tag {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip, $mac, $tags);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      'mac|m=s'         => \$mac,
      'tag|t=s'         => \$tags,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;

   exit_on_error_option($help)
      if $sector  eq ''
      or $tags       eq '';
   exit_on_error_option($help)
      if $mac        eq ''
      and $hostname  eq ''
      and $ip        eq '';

   $mac = normalize_mac_address($mac);

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   if ($tags !~ m/^ (?:\w+,)* \w+ $/xms) {
      die "Error: bad format for tags (comma separated list): $tags\n";
      }

   for my $tag (split/,/, $tags) {
      next if $tag eq 'universal';
      die "Error: TAG doesn't exist in the database. Create it before with create_tag: $tag\n" if not exists $computer_db->{'tag'}{$tag};
      }

   control_exist_sector($computer_db, $sector) or exit;

   $mac = get_mac_from_ip($computer_db, $sector, $ip, $mac)             if $ip ne '';
   $mac = get_mac_from_hostname($computer_db, $sector, $hostname, $mac) if $hostname ne '';
   control_syntax_mac_address($mac) or exit;

   LOOP_ON_COMPUTER:
   for my $computer (@{$computer_db->{$sector}}) {
      my ($mac_address, $attribute) = %{$computer};

      next LOOP_ON_COMPUTER if $mac_address ne $mac;

      $attribute->{'tag'}         = $tags;
      $attribute->{'modify_time'} = time;

      delete $attribute->{'tag'} if $tags eq 'universal';
      ipamdb_save("$COMPUTER_YAML", $computer_db);
      exit;
      }
   print "Mise à jour du commentaire de la machine [FAILED]\n";
   print "L'adresse MAC: $mac n'existe pas dans le domaine: $sector.\n";
   }

#-------------------------------------------------------------------------------
# ACTIVATION section
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#Nom: disable_pc
#Description: désactive une machine (du DHCP ou en IP statique, et du DNS) (champs enabled=non)

sub disable_pc {
   my ($hostname, $sector, $ip) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   if ($ip ne '') { # disable by IP
      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
      if ( control_exist_ip($computer_db, $ip) == 1 ) {
         die "Error: unkown IP address: $ip [FAILED]\n";
         }

      for my $sector_current (keys %{$computer_db}) {
         next if $sector_current eq 'dset';
         next if $sector_current eq 'pool';
         next if $sector_current eq 'pxe';
         next if $sector_current eq 'tag';
         next if $sector_current eq 'version';

         my @sectordb = @{$computer_db->{$sector_current}};
         LOOP_ON_COMPUTER:
         for my $computer (@sectordb) {
            my ($mac_address, $attribute) = %{$computer};

            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;

            if ($attribute->{'enabled'} eq 'no') {
               print "Info: IP $ip from sector $sector_current is already disable [OK]" .
                  " ... Status: $attribute->{'enabled'}\n";
               exit;
               }

            my $timestamp = time;
            $attribute->{'modify_time'} = $timestamp;
            $attribute->{'enabled'}     = 'no';
            ipamdb_save("$COMPUTER_YAML", $computer_db);
            print "Info: disabling IP $ip from sector $sector_current [OK]" .
               " ... Status: $attribute->{'enabled'}\n";
            exit;
            }
         }
      }
   else { # disable by Hostname
      control_exist_sector($computer_db, $sector);
      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
         die "Error: unkown host: $hostname, in sector: $sector [FAILED]\n";
         }

      LOOP_ON_COMPUTER:
      for my  $computer (@{$computer_db->{$sector}}) {
         my ($mac_address, $attribute) = %{$computer};

         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;

         if ($attribute->{'address_type'} eq 'pool-dhcp') {
            die "Error: host $hostname from sector $sector belongs to a a pool [FAILED]" .
               " ... use 'disable-float' command instead";
            }

         if ($attribute->{'enabled'} eq 'no') {
            print "Info: host $hostname from sector $sector is already disable [OK]" .
               " ... Status: $attribute->{'enabled'}\n";
            exit;
            }

         my $timestamp = time;
         $attribute->{'modify_time'} = $timestamp;
         $attribute->{'enabled'}     = 'no';
         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: disabling host $hostname from sector $sector [OK]" .
            " ... Status: $attribute->{'enabled'}\n";
         exit;
         }
      }
   }

#-------------------------------------------------------------------------------

sub disable_float {
   my ($pool, $mac) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   if ( control_exist_mac($computer_db, $mac) == 1 ) {
      die "Error: unkown physical MAC address: $mac [FAILED]\n";
      }

   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      my @sectordb = @{$computer_db->{$sector_current}};

      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};
         next LOOP_ON_COMPUTER if $mac_address ne $mac;

         if ($attribute->{'ip'} eq $pool) {
            if ($attribute->{'enabled'} eq 'no') {
               print "Info: host $mac from pool $pool is already disable [OK]" .
                  " ... Status: $attribute->{'enabled'}\n";
               exit;
               }
            my $timestamp = time;
            $attribute->{'modify_time'} = $timestamp;
            $attribute->{'enabled'}     = 'no';
            ipamdb_save("$COMPUTER_YAML", $computer_db);
            print "Info: disabling host $mac from pool $pool [OK]" .
               " ... Status: $attribute->{'enabled'}\n";
            exit;
            }
         else {
            die "Error: host disable $mac [FAILED]" .
               " ... The host $mac does not belong to the $pool pool.\n";
            }
         }
      }
   }

#-------------------------------------------------------------------------------
#Nom: enable_pc
#Description: active une machine désactivée(du DHCP ou en IP statique, et du DNS) (champs enabled=non)

sub enable_pc {
   my ($hostname, $sector, $ip) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   control_exist_sector($computer_db, $sector) or exit;

   if ($ip ne '') { # enable by IP
      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
      if ( control_exist_ip($computer_db, $ip) == 1 ) {
         print "Error: unkown IP address: $ip\n";
         exit;
         }

      for my $sector_current (keys %{$computer_db}) {
         next if $sector_current eq 'dset';
         next if $sector_current eq 'pool';
         next if $sector_current eq 'pxe';
         next if $sector_current eq 'tag';
         next if $sector_current eq 'version';

         my @sectordb = @{$computer_db->{$sector_current}};

         LOOP_ON_COMPUTER:
         for my $computer (@sectordb) {
            my ($mac_address, $attribute) = %{$computer};
            if ($attribute->{'ip'} eq $ip) {

               if ($attribute->{'enabled'} eq 'yes') {
                  print "Info: IP $ip belongs to sector $sector is already enable [OK]" .
                     " ... Status: $attribute->{'enabled'}\n";
                  exit;
                  }

               my $timestamp = time;
               $attribute->{'modify_time'} = $timestamp;
               $attribute->{'enabled'}     = 'yes';
               ipamdb_save("$COMPUTER_YAML", $computer_db);
               print "Info: IP $ip is now enable [OK]" .
                  " ... Status: $attribute->{'enabled'}\n";
               exit;
               }
            }
         }
      }
   else { # enable by Hostname
      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
         die "Error: unkown host: $hostname, in sector: $sector\n";
         }

      LOOP_ON_COMPUTER:
      for my $computer (@{$computer_db->{$sector}}) {
         my ($mac_address, $attribute) = %{$computer};
         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;

         if ($attribute->{'address_type'} eq 'pool-dhcp') {
            die "Error: host $hostname from sector $sector belongs to a a pool [FAILED]" .
               " ... use 'enable-float' command instead";
            }

         if ($attribute->{'enabled'} eq 'yes') {
            print "Info: host $hostname belongs to sector $sector is already enable [OK]" .
               " ... Status: $attribute->{'enabled'}\n";
            exit;
            }

         my $timestamp = time;
         $attribute->{'modify_time'} = $timestamp;
         $attribute->{'enabled'}     = 'yes';
         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: host $hostname is now enable [OK]" .
            " ... Status: $attribute->{'enabled'}\n";
         exit;
         }
      }
   }

#-------------------------------------------------------------------------------

sub enable_float {
   my ($pool, $mac) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   if ( control_exist_mac($computer_db, $mac) == 1 ) {
      die "Error: unkown physical MAC address: $mac [FAILED]\n";
      }

   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      my @sectordb = @{$computer_db->{$sector_current}};

      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};
         next LOOP_ON_COMPUTER if $mac_address ne $mac;

         if ($attribute->{'ip'} ne $pool) {
            die "Error: host enable $mac [FAILED]" .
               " ... The host $mac does not belong to the $pool pool.\n";
            }

         if ($attribute->{'enabled'} eq 'yes') {
            print "Info: host $mac from pool $pool is already enable [OK]" .
               " ... Status: $attribute->{'enabled'}\n";
            exit;
            }

         my $timestamp = time;
         $attribute->{'modify_time'} = $timestamp;
         $attribute->{'enabled'}     = 'yes';
         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: enabling host $mac from pool $pool [OK]" .
            " ... Status: $attribute->{'enabled'}\n";
         exit;
         }
      }
   }

#-------------------------------------------------------------------------------

sub cmd_enable_pc {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $sector  eq '';
   exit_on_error_option($help)
      if $hostname   eq ''
      and $ip        eq '';
   exit_on_error_option($help)
      if $hostname   ne ''
      and $ip        ne '';

   enable_pc($hostname, $sector, $ip);
   }

#-------------------------------------------------------------------------------

sub cmd_disable_pc {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $sector  eq '';
   exit_on_error_option($help)
      if $hostname   eq ''
      and $ip        eq '';
   exit_on_error_option($help)
      if $hostname   ne ''
      and $ip        ne '';

   disable_pc($hostname, $sector, $ip);
   }

#-------------------------------------------------------------------------------

sub cmd_disable_float {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($pool, $mac);

   GetOptions(
      'pool|p=s'  => \$pool,
      'mac|m=s'   => \$mac,
      );

   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
   exit_on_error_option($help)
      if $pool eq ''
      or $mac  eq '';

   disable_float($pool, $mac);
   }

#-------------------------------------------------------------------------------

sub cmd_enable_float {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($pool, $mac);

   GetOptions(
      'pool|p=s'  => \$pool,
      'mac|m=s'   => \$mac,
      );

   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
   exit_on_error_option($help)
      if $pool eq ''
      or $mac  eq '';

   enable_float($pool, $mac);
   }

#-------------------------------------------------------------------------------
# DELETE section
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#Nom: del_pc
#Description: supprime une machine en DHCP ou en IP statique.

sub del_pc {
   my ($hostname, $sector, $ip) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   control_exist_sector($computer_db, $sector) or exit;
   if ($ip ne '') { # delete by IP
      if ( control_exist_ip($computer_db, $ip) == 1 ) {
         die "Error: unkown IP address: $ip\n";
         }

      my $computer_index = 0;

      LOOP_ON_COMPUTER:
      for my $computer (@{$computer_db->{$sector}}) {
         my ($mac_address, $attribute) = %{$computer};

         $computer_index++, next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
         
         splice(@{$computer_db->{$sector}}, $computer_index => 1);
         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: host $ip has been removed from the sector $sector [OK]\n";
         exit;
         }
      }
   else {
      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
         die "Error: unkown host: $hostname, in sector: $sector\n";
         }

      my $computer_index = 0;

      LOOP_ON_COMPUTER:
      for my $computer (@{$computer_db->{$sector}}) {
         my ($mac_address, $attribute) = %{$computer};

         $computer_index++, next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;

         if ($attribute->{'address_type'} eq 'pool-dhcp') {
            die "Error: host remove $hostname from the sector $sector [FAILED]" .
               " ... The host $hostname belongs to a DHCP pool.\n";
            }

         splice(@{$computer_db->{$sector}}, $computer_index => 1);
         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: host $hostname has been removed from the sector $sector [OK]\n";
         exit;
         }
      }
   }

#-------------------------------------------------------------------------------
#Nom: del_float
#Description: supprime une machine d'un pool DHCP

sub del_float {
   my ($pool, $mac) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   if ( control_exist_mac($computer_db, $mac) == 1 ) {
      print "Adresse MAC $mac non trouvée.\n";
      exit;
      }

   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      my @sectordb = @{$computer_db->{$sector_current}};

      my $computer_index = 0;

      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};

         $computer_index++, next LOOP_ON_COMPUTER if $mac_address ne $mac;

         if ($attribute->{'ip'} ne $pool) {
            die "Error: host remove $mac [FAILED]" .
               " ... The host $mac does not belong to the $pool pool.\n";
            }

         splice(@{$computer_db->{$sector_current}}, $computer_index => 1);
         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: remove host $mac from the pool $pool [OK]\n";
         exit;
         }
      }
   }

#-------------------------------------------------------------------------------

sub cmd_del_pc {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $sector  eq '';
   exit_on_error_option($help)
      if $hostname   eq ''
      and $ip        eq '';
   exit_on_error_option($help)
      if $hostname   ne ''
      and $ip        ne '';

   del_pc($hostname, $sector, $ip);
   }

#-------------------------------------------------------------------------------

sub cmd_del_float {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($pool, $mac);

   GetOptions(
      'pool|p=s'        => \$pool,
      'mac|m=s'         => \$mac,
      );

   ($pool) = split /\./, $pool, 2 if $pool =~ m/\./;
   exit_on_error_option($help)
      if $pool eq ''
      or $mac  eq '';

   del_float($pool, $mac);
   }

#-------------------------------------------------------------------------------
# SECTOR section
#-------------------------------------------------------------------------------

sub cmd_create_sector {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($sector, $dns_extension, $comment);

   GetOptions(
      'sector|s|d=s'       => \$sector,
      'dns-extension|e=s'  => \$dns_extension,
      'comment|c=s'        => \$comment,
      );

   exit_on_error_option($help)
      if $sector        eq ''
      or $dns_extension eq ''
      or $comment       eq '';

   $comment = normalize_comment($comment);

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   $computer_db->{'dset'} ||= {};
   die "Error: sector already exists: $sector\n" if exists $computer_db->{'dset'}{$sector};

   control_syntax_comment($comment)    or exit;

   my $timestamp = time;
   $computer_db->{'dset'}{$sector} = {
      'dns_extension'   => $dns_extension,
      'comment'         => $comment,
      'create_time'     => $timestamp,
      'modify_time'     => $timestamp,
      };
   $computer_db->{$sector} ||= []; # Create empty sector computer list by default
   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------

sub cmd_sector_add_ip {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($sector, $ip_range);

   GetOptions(
      'sector|s|d=s' => \$sector,
      'ip-range|i=s' => \$ip_range,
      );

   exit_on_error_option($help)
      if $sector     eq ''
      or $ip_range   eq '';

   control_syntax_cidr($ip_range)   or die "Error: bad IP range $ip_range syntax (CIDR)\n";;

   my $computer_db = ipamdb_load($COMPUTER_YAML);
   exists $computer_db->{'dset'}{$sector} or die "Error: sector not exists: $sector\n";

   my $timestamp = time;
   $computer_db->{'dset'}{$sector}{'ip-range'} ||= [];
   LOOP_ON_CIDR:
   for my $cidr_current (@{$computer_db->{'dset'}{$sector}{'ip-range'}}) {
      next LOOP_ON_CIDR if $cidr_current ne $ip_range;
      
      die "Error: IP range $ip_range already in sector $sector\n";
      }

   my $timestamp = time;
   push @{$computer_db->{'dset'}{$sector}{'ip-range'}}, $ip_range;
   $computer_db->{'dset'}{$sector}{'modify_time'} = $timestamp;
   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------
# POOL section
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#Nom: create_pool
#Description: crée un pool dans le fichier de données YAML et dans le DHCP.
#
#Commentaires: il y a un petit bug si jamais on rentre que des adresses ip qui existent déjà.
#              Le pool est créé mais sans adresses ip.

sub cmd_create_pool {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($pool, $sector, $file_pool, $ipaddress_pool);

   GetOptions(
      'pool|p=s'           => \$pool,
      'sector|s|d=s'      => \$sector,
      'file-pool|f=s'      => \$file_pool,
      'ipaddress-pool|i=s' => \$ipaddress_pool,
      );

   exit_on_error_option($help)
      if $pool             eq ''
      or $sector        eq ''
      or $file_pool        eq ''
      or $ipaddress_pool   eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   if ($computer_db->{'pool'}) {
      die "Error: pool already exists: $pool\n" if exists $computer_db->{'pool'}{$pool};
      }

   #--- control if the domain's pool exist ---#
   control_exist_sector($computer_db, $sector) or exit;

   my @ip_list = ();
   #---control if address exist ---#
   if ($ipaddress_pool =~ /,/) {
      LOOP_ON_IP:
      for my $ip (split /,/, $ipaddress_pool) {
         if ($ip =~ /-/) {
            my ($ip1, $ip2, $ip3, $range) = split /\./, $ip;
            my ($first, $last) = split /-/, $range;
            for (my $cpt = $first; $cpt <= $last; $cpt++) {
               my $ip_loc = "$ip1.$ip2.$ip3.$cpt";
               control_syntax_ip($ip_loc) or die "Error: bad IP syntax: $ip_loc\n";
               control_exist_ip($computer_db, $ip_loc) or die "Error: IP address already exists: $ip_loc\n";
               push @ip_list, $ip_loc;
               }
            }
         else {
            control_syntax_ip($ip) or next LOOP_ON_IP;
            if ( control_exist_ip($computer_db, $ip) == 0 ) {
               print "L'adresse IP $ip existe déjà\n";
               next;
               }
            push @ip_list, $ip;
            }
         }
      }

   my $timestamp = time;
   $computer_db->{'pool'}{$pool} = {
      'ip'          => [@ip_list],
      'enabled'     => 'yes',
      'create_time' => $timestamp,
      'modify_time' => $timestamp,
      'file'        => $file_pool,
      'domain'      => $sector,
      };
   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------

sub cmd_show_pool {
   local @ARGV = @_;

   my ($no_header);

   GetOptions(
      'no-header|H' => \$no_header,
      );

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   printf "%-17s %-17s %s\n", 'Pool', 'File', 'DNS-Domain' if not $no_header;
   LOOP_ON_PXE:
   for my $pool ( keys %{$computer_db->{'pool'}} ) {

      printf "%-17s %-17s %s\n",
         $pool,
         $computer_db->{'pool'}{$pool}{'file'},
         $computer_db->{'pool'}{$pool}{'domain'},
      }
   }

#-------------------------------------------------------------------------------
# PXE section
#-------------------------------------------------------------------------------

sub cmd_create_pxe {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($pxe_config, $ip_next_server, $filename, $comment);

   GetOptions(
      'bootp|b=s'       => \$pxe_config,
      'next-server|n=s' => \$ip_next_server,
      'filename|f=s'    => \$filename,
      'comment|c=s'     => \$comment,
      );

   exit_on_error_option($help)
      if $pxe_config       eq ''
      or $ip_next_server   eq ''
      or $filename         eq ''
      or $comment          eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   $comment = normalize_comment($comment);

   $computer_db->{'pxe'} ||= {};
   die "Error: PXE config already exists: $pxe_config\n" if exists $computer_db->{'pxe'}{$pxe_config};

   control_syntax_ip($ip_next_server)  or die "Error: bad IP syntax: $ip_next_server\n";
   control_syntax_comment($comment)    or exit;

   my $timestamp = time;
   $computer_db->{'pxe'}{$pxe_config} = {
      'ip_next_server'  => $ip_next_server,
      'filename'        => $filename,
      'comment'         => $comment,
      'create_time'     => $timestamp,
      'modify_time'     => $timestamp,
      };
   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------

sub cmd_remove_pxe {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($pxe_config);

   GetOptions(
      'bootp|b=s' => \$pxe_config,
      );

   exit_on_error_option($help)
      if $pxe_config eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   $computer_db->{'pxe'} ||= {};
   die "Error: PXE config does not exist: $pxe_config\n" if not exists $computer_db->{'pxe'}{$pxe_config};

   # Test if some computer use this config
   LOOP_ON_SECTOR:
   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      LOOP_ON_COMPUTER:
      for my $computer (@{$computer_db->{$sector_current}}) {
         my ($mac_address, $attribute) = %{$computer};

         if (exists $attribute->{'pxe_config'}) {
            my $hostname = $attribute->{'hostname'};
            die "Error: computer still use this PXE config: $hostname.$sector_current $mac_address\n" if $pxe_config eq $attribute->{'pxe_config'};
            }
         }
      }

   delete $computer_db->{'pxe'}{$pxe_config};
   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#--------------------------------------------------------------------------------

sub cmd_show_pxe {
   local @ARGV = @_;

   my ($no_header);

   GetOptions(
      'no-header|H' => \$no_header,
      );

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   printf "%-12s %-13s %-30s %s\n", 'PXE-Config', 'Next-Server', 'Filename', 'Comment' if not $no_header;
   LOOP_ON_PXE:
   for my $pxe_config ( keys %{$computer_db->{'pxe'}} ) {
      my $ip_next_server = $computer_db->{'pxe'}{$pxe_config}{'ip_next_server'};
      my $filename       = $computer_db->{'pxe'}{$pxe_config}{'filename'};
      my $comment        = $computer_db->{'pxe'}{$pxe_config}{'comment'};

      printf "%-12s %-13s %-30s %s\n", $pxe_config, $ip_next_server, $filename, $comment;
      }
   }

#-------------------------------------------------------------------------------

sub cmd_enable_pxe {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip, $pxe_config);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      'bootp|b=s'       => \$pxe_config,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $sector  eq ''
      or $pxe_config eq '';
   exit_on_error_option($help)
      if $hostname   eq ''
      and $ip        eq '';
   exit_on_error_option($help)
      if $hostname   ne ''
      and $ip        ne '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   die "Error: PXE config not exists: $pxe_config\n" if not exists $computer_db->{'pxe'}{$pxe_config};

   control_exist_sector($computer_db, $sector) or exit;
   if ($ip ne '') {
      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
      if ( control_exist_ip($computer_db, $ip) == 1 ) {
         die "Error: unkown IP address: $ip\n";
         }

      for my $sector_current (keys %{$computer_db}) {
         next if $sector_current eq 'dset';
         next if $sector_current eq 'pool';
         next if $sector_current eq 'pxe';
         next if $sector_current eq 'tag';
         next if $sector_current eq 'version';

         LOOP_ON_COMPUTER:
         for my $computer (@{$computer_db->{$sector_current}}) {
            my ($mac_address, $attribute) = %{$computer};
            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;

            $attribute->{'modify_time'} = time;
            $attribute->{'pxe_config'}  = $pxe_config;
            ipamdb_save("$COMPUTER_YAML", $computer_db);
            print "Info: host $attribute->{'hostname'} ($sector_current), IP $ip, PXE enabled: $pxe_config\n";
            exit;
            }
         }
      }
   else {
      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
         die "Error: unkown host: $hostname, in sector: $sector\n";
         }

      LOOP_ON_COMPUTER:
      for my $computer (@{$computer_db->{$sector}}) {
         my ($mac_address, $attribute) = %{$computer};
         next LOOP_ON_COMPUTER if $attribute->{'hostname'} ne $hostname;
         
         if ($attribute->{'address_type'} eq 'pool-dhcp') {
            die "Error. Host $hostname ($sector) in a pool. No PXE possible [FAILED]\n";
            }

         $attribute->{'modify_time'} = time;
         $attribute->{'pxe_config'}  = $pxe_config;
         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: host $hostname ($sector), IP $attribute->{'ip'}, PXE enabled: $pxe_config [OK]\n";
         exit;
         }
      }
   }

#-------------------------------------------------------------------------------

sub cmd_disable_pxe {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($hostname, $sector, $ip);

   GetOptions(
      'hostname|h=s'    => \$hostname,
      'sector|s|d=s'    => \$sector,
      'ip|i=s'          => \$ip,
      );

   ($hostname, $sector) = split /\./, $hostname, 2 if $hostname =~ m/\./;
   exit_on_error_option($help)
      if $sector  eq '';
   exit_on_error_option($help)
      if $hostname   eq ''
      and $ip        eq '';
   exit_on_error_option($help)
      if $hostname   ne ''
      and $ip        ne '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   control_exist_sector($computer_db, $sector) or exit;
   if ($ip ne '') {
      control_syntax_ip($ip) or die "Error: bad IP syntax $ip\n";;
      if ( control_exist_ip($computer_db, $ip) == 1 ) {
         die "Error: unkown IP address: $ip\n";
         }

      for my $sector_current (keys %{$computer_db}) {
         next if $sector_current eq 'dset';
         next if $sector_current eq 'pool';
         next if $sector_current eq 'pxe';
         next if $sector_current eq 'tag';
         next if $sector_current eq 'version';

         LOOP_ON_COMPUTER:
         for my $computer (@{$computer_db->{$sector_current}}) {
            my ($mac_address, $attribute) = %{$computer};
            
            next LOOP_ON_COMPUTER if $attribute->{'ip'} ne $ip;
            next LOOP_ON_COMPUTER if not exists $attribute->{'pxe_config'};

            my $pxe_config = $attribute->{'pxe_config'};
            $attribute->{'modify_time'} = time;
            delete $attribute->{'pxe_config'};
            ipamdb_save("$COMPUTER_YAML", $computer_db);
            print "Info: IP address: $ip, PXE disable from config: $pxe_config [OK]\n";
            exit;
            }
         }
      }
   else {
      if ( control_exist_hostname($computer_db, $sector, $hostname) == 1 ) {
         die "Error: unkown host: $hostname, in sector: $sector\n";
         }

      LOOP_ON_COMPUTER:
      for my $computer (@{$computer_db->{$sector}}) {
         my ($mac_address, $attribute) = %{$computer};

         next LOOP_ON_COMPUTER if $attribute->{'hostname'} eq $hostname;

         if ($attribute->{'address_type'} eq 'pool-dhcp') {
            die "Error: host $hostname ($sector) in a pool. No PXE possible [FAILED]\n";
            }

         next LOOP_ON_COMPUTER if not exists $attribute->{'pxe_config'};

         my $pxe_config = $attribute->{'pxe_config'};
         $attribute->{'modify_time'} = time;
         delete $attribute->{'pxe_config'};
         ipamdb_save("$COMPUTER_YAML", $computer_db);
         print "Info: host $hostname ($sector), PXE disable from config: $pxe_config [OK]\n";
         exit;
         }
      }
   }

#-------------------------------------------------------------------------------
# TAG section
#-------------------------------------------------------------------------------

sub cmd_create_tag {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($tag, $comment);

   GetOptions(
      'tag|t=s'      => \$tag,
      'comment|c=s'  => \$comment,
      );

   exit_on_error_option($help)
      if $tag     eq ''
      or $comment eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   $comment = normalize_comment($comment);

   $computer_db->{'tag'} ||= {};
   die "Error: TAG already exists: $tag\n" if exists $computer_db->{'tag'}{$tag};

   die "Error: TAG 'universal' is intrinsic. It's not possible to create it.\n" if $tag eq 'universal';

   if ($tag !~ m/^ \w+ $/xms) {
      die "Error: bad format for TAG (alphanumeric string): $tag\n";
      }

   control_syntax_comment($comment) or exit;

   my $timestamp = time;
   $computer_db->{'tag'}{$tag} = {
      'comment'         => $comment,
      'create_time'     => $timestamp,
      'modify_time'     => $timestamp,
      };
   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------

sub cmd_remove_tag {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($tag);

   GetOptions(
      'tag|t=s' => \$tag,
      );

   exit_on_error_option($help)
      if $tag eq '';

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   $computer_db->{'tag'} ||= {};
   die "Error: TAG does not exist: $tag\n" if not exists $computer_db->{'tag'}{$tag};

   # Test if some computer use this config
   LOOP_ON_SECTOR:
   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      LOOP_ON_COMPUTER:
      for my $computer (@{$computer_db->{$sector_current}}) {
         my ($mac_address, $attribute) = %{$computer};

         if (exists $attribute->{'tag'}) {
            my $hostname = $attribute->{'hostname'};
            die "Error: computer still use this TAG: $hostname.$sector_current $mac_address\n" if $tag eq $attribute->{'tag'};
            }
         }
      }

   delete $computer_db->{'tag'}{$tag};
   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#--------------------------------------------------------------------------------

sub cmd_show_tag {
   local @ARGV = @_;

   my ($no_header);

   GetOptions(
      'no-header|H' => \$no_header,
      );

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   printf "%-12s %s\n", 'TAG', 'Comment' if not $no_header;
   LOOP_ON_TAG:
   for my $tag ( keys %{$computer_db->{'tag'}} ) {
      my $comment = $computer_db->{'tag'}{$tag}{'comment'};

      printf "%-12s %s\n", $tag, $comment;
      }
   }

#--------------------------------------------------------------------------------
# GLOBAL section
#--------------------------------------------------------------------------------

sub cmd_upgrade_db {
   my $flag_change;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   LOOP_ON_SECTOR:
   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      my @sectordb = @{$computer_db->{$sector_current}};

      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};
         my $new_mac = normalize_mac_address($mac_address);
         print "perl -pi -e 's/$mac_address:/$new_mac:/' $COMPUTER_YAML\n" if "$mac_address" ne "$new_mac";

         my $comment = $attribute->{'comment'};
         $comment =~ s/\s\s+/ /g and $flag_change++;
         $comment =~ s/^\s+\S//  and $flag_change++;
         $comment =~ s/\S\s+$//  and $flag_change++;
         $comment =~ s{^(\d\d\d\d)\/O(\d\/\d\d)}{$1/0$2} and $flag_change++;
         $comment =~ s{^(\d\d\d\d\/\d\d\/)O(\d)}{$1/0$2} and $flag_change++;
         $comment =~ s{^(\d\d\d\d)\/(\d\d)\/(\d\d)}{$1-$2-$3} and $flag_change++;
         if ($comment !~ m/^\d\d\d\d-\d\d-\d\d/) {
            print "# no date at beginning of comment $mac_address\n";
            }

         $attribute->{'comment'} = $comment;
         }
      }
   print "# FLAG :$flag_change\n";

   ipamdb_save("$COMPUTER_YAML", $computer_db) if $flag_change;
   }

#--------------------------------------------------------------------------------

sub cmd_show_sector {
   local @ARGV = @_;

   my ($no_header);

   GetOptions(
      'no-header|H' => \$no_header,
      );

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   my $tb_computer = Text::Table->new(
     {align  => 'left',   align_title => 'left',   title => 'Sector'},
     {is_sep => 1,        body        => '  '},
     {align  => 'left',   align_title => 'left',   title => 'DNS-Extension'},
     {is_sep => 1,        body        => '  '},
     {align  => 'left',   align_title => 'left',   title => 'IP-Range'},
     {align  => 'left',   align_title => 'left',   title => 'Date'},
     {align  => 'left',   align_title => 'left',   title => 'Comment'},
     {align  => 'left',   align_title => 'left',   title => 'Category'},
     );

   LOOP_ON_SECTOR:
   for my $sector_current (sort keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      $tb_computer->add($sector_current), next LOOP_ON_SECTOR if not exists $computer_db->{'dset'}{$sector_current};

      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computer_db->{'dset'}{$sector_current}{'modify_time'};
      $year += 1900;
      $mon++;
      my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;

      my $ip_range;
      $ip_range = join ',', @{$computer_db->{'dset'}{$sector_current}{'ip-range'}} if exists $computer_db->{'dset'}{$sector_current}{'ip-range'};

      my $category;
      my $comment = $computer_db->{'dset'}{$sector_current}{'comment'};
      $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;
      $comment =~ s/\s+(\(\w+\))$// and $category = $1;

      $tb_computer->add($sector_current,
         $computer_db->{'dset'}{$sector_current}{'dns_extension'},
         $ip_range,
         $date,
         $comment,
         $category,
         );
      }

   print $tb_computer->title(),
         $tb_computer->rule('-') if not $no_header;
   print $tb_computer->body();
   }

#--------------------------------------------------------------------------------

sub cmd_search_mac {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($mac);

   GetOptions(
      'mac|m=s' => \$mac,
      );

   exit_on_error_option($help)
      if $mac eq '';

   $mac = normalize_mac_address($mac);

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   control_syntax_mac_address($mac) or exit;

   LOOP_ON_SECTOR:
   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      my @sectordb = @{$computer_db->{$sector_current}};

      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};

         next LOOP_ON_COMPUTER if $mac_address ne $mac;

         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $attribute->{'modify_time'};
         $year += 1900;
         $mon++;
         my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;

         my $comment = $attribute->{'comment'};
         $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;

         my $enable = $attribute->{'enabled'};
         if (exists $attribute->{'pxe_config'}) {
            $enable .= '/' . $attribute->{'pxe_config'};
            }
         if (exists $attribute->{'tag'}) {
            $enable .= ':' . $attribute->{'tag'};
            }

         printf "%-30s  %-20s %17s %9s %3s %10s %s\n",
            $attribute->{'hostname'} . '.' . $sector_current,
            $attribute->{'ip'},
            $mac_address,
            $attribute->{'address_type'},
            $enable,
            $date,
            $comment;
         }
      }
   }

#--------------------------------------------------------------------------------
#Nom: show
#Description: liste les machines à partir du fichier YAML par nom de domaine.

sub cmd_show_host {
   my %ipdb = ();

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   my $tb_computer = Text::Table->new(
     {align  => 'left',   align_title => 'left',   title => 'Hostname.Sector'},
     {is_sep => 1,        body        => '  '},
     {align  => 'left',   align_title => 'left',   title => 'IPv4-Address'},
     {is_sep => 1,        body        => '  '},
     {align  => 'center', align_title => 'center', title => 'MAC-Address'},
     {is_sep => 1,        body        => '  '},
     {align  => 'right',  align_title => 'right',  title => 'Type'},
     {align  => 'right',  align_title => 'right',  title => 'Status'},
     {is_sep => 1,        body        => '  '},
     {align  => 'left',   align_title => 'left',   title => 'Date'},
     {align  => 'left',   align_title => 'left',   title => 'Comment'},
     );

  LOOP_ON_SECTOR:
   for my $sector_current (sort keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      my @sectordb = @{$computer_db->{$sector_current}};

      LOOP_ON_COMPUTER:
      for my $computer (@sectordb) {
         my ($mac_address, $attribute) = %{$computer};
         my $ip = $attribute->{'ip'};

         if ($ip =~ m/$DDT::RE::IPv4_ADDRESS/xms) {
            if ( not exists $ipdb{$ip} ) {
               $ipdb{$ip} = {
                  'mac_address'  => $mac_address,
                  %{$attribute},
                  'sector'    => $sector_current,
                  };
               }
            else {
               print {*STDERR} "# Warning: $ip already exists in the database with MAC $mac_address!\n";
               }
            next LOOP_ON_COMPUTER;
            }

         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $attribute->{'modify_time'};
         $year += 1900;
         $mon++;
         my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;

         my $comment = normalize_comment($attribute->{'comment'});
         $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;

         my $enable = $attribute->{'enabled'};
         if (exists $attribute->{'pxe_config'}) {
            $enable .= '/' . $attribute->{'pxe_config'};
            }
         if (exists $attribute->{'tag'}) {
            $enable .= ':' . $attribute->{'tag'};
            }

         #printf "%-30s  %-20s %17s %9s %3s %10s %s\n",
         $tb_computer->add(
            $attribute->{'hostname'} . '.' . $sector_current,
            $ip,
            $mac_address,
            $attribute->{'address_type'},
            $enable,
            $date,
            $comment,
            );
         }
      #print "\n# *** List of pool computers in the sector: $sector_current ***\n";
      }

   #print "\n# *** List of computers ordered by IP and sector ***\n";
   LOOP_ON_IP_ADDRESS:
   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %ipdb)) {
      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $ipdb{$ip}->{'modify_time'};
      $year += 1900;
      $mon++;
      my $date = sprintf '%04i-%02i-%02i', $year, $mon, $mday;

      my $comment =$ipdb{$ip}->{'comment'};
      $comment =~ s/^\d\d\d\d-\d\d-\d\d\s//;

      my $enable = $ipdb{$ip}->{'enabled'};
      if (exists $ipdb{$ip}->{'pxe_config'}) {
         $enable .= '/' . $ipdb{$ip}->{'pxe_config'};
         }
      if (exists $ipdb{$ip}->{'tag'}) {
         $enable .= ':' . $ipdb{$ip}->{'tag'};
         }

      #printf "%-30s %-20s %17s %9s %3s %10s %s\n",
      $tb_computer->add(
         $ipdb{$ip}->{'hostname'} . '.' . $ipdb{$ip}->{'sector'},
         $ip,
         normalize_mac_address($ipdb{$ip}->{'mac_address'}),
         $ipdb{$ip}->{'address_type'},
         $enable,
         $date,
         $comment
         );
      }

   print $tb_computer->title();
   print $tb_computer->rule('-');
   print $tb_computer->body();
   }

#-------------------------------------------------------------------------------
#Nom: cmd_generate_dhcp_file
#Description: génère les fichiers de configuration des machines et des pools du dhcp

sub cmd_generate_dhcp_file {
   backup_database();

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   my %file_pool;

   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      open FILE_VLAN, '>', "$FOLDER_GEN_DHCP/$sector_current";
      my @sectordb = @{$computer_db->{$sector_current}};
      for my $value (@sectordb) {
         ALL_MAC_ADDRESS:
         for my $mac_addres (keys %{$value}) {
            #host pcdavoust {  deny-unknown-clients;
            #hardware ethernet 0:6:5b:b8:13:d1;
            #fixed-address 194.254.66.72;
            #}

            my $hostname     = $value->{$mac_addres}{'hostname'};
            my $ip           = $value->{$mac_addres}{'ip'};
            my $comment      = $value->{$mac_addres}{'comment'};
            my $address_type = $value->{$mac_addres}{'address_type'};
            my $enabled      = $value->{$mac_addres}{'enabled'};
            my $tags         = $value->{$mac_addres}{'tag'} || 'universal';

            my $buffer;
            if ($address_type eq 'dhcp') {
               if ($enabled eq 'yes') {
                  $buffer  = "host $hostname {\n"; # deny-unknown-clients;
                  $buffer .= "   hardware ethernet $mac_addres;\n";
                  $buffer .= "   fixed-address $ip;\n";

                  if (exists $value->{$mac_addres}{'pxe_config'}) {
                     my $pxe_config     = $value->{$mac_addres}{'pxe_config'};
                     my $ip_next_server = $computer_db->{'pxe'}{$pxe_config}{'ip_next_server'};
                     my $filename       = $computer_db->{'pxe'}{$pxe_config}{'filename'};
                     $buffer .= "   next-server $ip_next_server;\n";
                     $buffer .= "   filename \"$filename\";\n";
                     }
                  $buffer .= "   #comment: $comment\n";
                  $buffer .= "   }\n";
                  $buffer .= "\n";

                  for my $tag (split/,/, $tags) {
                     $file_pool{"tag-$tag"} ||= [];
                     push @{$file_pool{"tag-$tag"}}, "subclass \"tag-$tag\" 1:$mac_addres; # $comment\n";
                     }
                  }
               else {
                  $buffer  = "#host $hostname {\n"; # deny-unknown-clients;
                  $buffer .= "#   hardware ethernet $mac_addres;\n";
                  $buffer .= "#   fixed-address $ip;\n";
                  $buffer .= "#   comment: $comment \n";
                  $buffer .= "#   }\n";
                  $buffer .= "\n";
                  }
               print FILE_VLAN $buffer;
               }
            elsif ($address_type eq 'pool-dhcp') {
               #--- Génère les fichiers pool dhcp ---#
               for my $current_pool (keys %{$computer_db->{'pool'}}) {
                  next if $current_pool ne $ip;

                  if ($enabled eq 'yes') {
                     $buffer = "subclass \"$current_pool\" 1:$mac_addres; # $comment\n";

                     for my $tag (split/,/, $tags) {
                        $file_pool{"tag-$tag"} ||= [];
                        push @{$file_pool{"tag-$tag"}}, "subclass \"tag-$tag\" 1:$mac_addres; # $comment\n";
                        }
                     }
                  else {
                     $buffer = "#subclass \"$current_pool\" 1:$mac_addres; # $comment\n";
                     }

                  my $current_pool_file_name = $computer_db->{'pool'}{$current_pool}{'file'};

                  $file_pool{$current_pool_file_name} ||= [];
                  push @{$file_pool{$current_pool_file_name}}, $buffer;
                  }
               }
            }
         }

      close FILE_VLAN;

      for my $file_name (keys %file_pool) {
         open FILE_POOL, '>', "$FOLDER_GEN_DHCP/$file_name";
         print FILE_POOL @{$file_pool{$file_name}};
         close FILE_POOL;
         }
      }
      print "Copy DHCP files from $FOLDER_GEN_DHCP to /etc/dhcp/include/\n";
      exec $SCRIPT_UPDATE;
   }

#-------------------------------------------------------------------------------
#Nom: cmd_generate_dns_file
#Description: génère les fichiers d'enregistrements DNS

sub cmd_generate_dns_file {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($verbose);

   GetOptions(
      'verbose|v' => \$verbose,
      );

   my $buffer_fwd;
   my $buffer_rev;
   my $pool_domain;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   for my $sector_current (keys %{$computer_db}) {
      next if $sector_current eq 'dset';
      next if $sector_current eq 'pool';
      next if $sector_current eq 'pxe';
      next if $sector_current eq 'tag';
      next if $sector_current eq 'version';

      if ($sector_current eq 'pool') {
         LOOP_ON_COMPUTER:
         for my $computer (@{$computer_db->{$sector_current}}) {
            for my $pool_name (keys %{$computer}) {
               $pool_domain = $computer->{$pool_name}->{'domain'}."\n";
               #print $computer->{$pool_name}->{'file'};
               chomp $pool_domain;
               open FILE_FORWARD_DNS, '>>', "$FOLDER_GEN_DNS/db.$pool_domain.fwd";
               open FILE_REVERSE_DNS, '>>', "$FOLDER_GEN_DNS/db.$pool_domain.rev";
               my @T_pool_ip = @{$computer->{$pool_name}->{'ip'}};
               for my $pool_ip (@T_pool_ip) {
                  my @T_split = split(/\./ , $pool_ip);
                  $buffer_fwd = sprintf "%-24s IN  A  %-15s ;\n", "$pool_name$T_split[3]", $pool_ip;
                  $buffer_rev = "$T_split[3]   IN PTR   $pool_name$T_split[3].$pool_domain.\n";
                  print FILE_FORWARD_DNS $buffer_fwd;
                  print FILE_REVERSE_DNS $buffer_rev;
                  }
               close FILE_FORWARD_DNS;
               close FILE_REVERSE_DNS;
               }
            }
         }

      else {
         #--- Création du fichier non-reverse ---#
         open FILE_FORWARD_DNS, ">> $FOLDER_GEN_DNS/db.$sector_current.fwd";
         open FILE_REVERSE_DNS, ">> $FOLDER_GEN_DNS/db.$sector_current.rev";

         my @sectordb = @{$computer_db->{$sector_current}};

         LOOP_ON_COMPUTER:
         for my $computer (@sectordb) {
            my ($mac_address, $attribute) = %{$computer};

            #host pcdavoust {  deny-unknown-clients;
            #hardware ethernet 0:6:5b:b8:13:d1;
            #fixed-address 194.254.66.72;
            #}

            my $hostname     = $attribute->{'hostname'};
            my $ip           = $attribute->{'ip'};
            my $comment      = $attribute->{'comment'};
            my $address_type = $attribute->{'address_type'};
            my $enabled      = $attribute->{'enabled'};

            next LOOP_ON_COMPUTER if not (($address_type eq 'dhcp') or ($address_type eq 'static'));

            my $dns_domain = $sector_current;
            if (exists $computer_db->{'dset'}{$sector_current}) {
               $dns_domain = $computer_db->{'dset'}{$sector_current}{'dns_extension'};
               }

            my @ip_split = split /\./, $ip;
            if ($enabled eq 'yes') {
               if (exists $attribute->{'dns_extension'}
                     and "$attribute->{'dns_extension'}" != "$dns_domain") {
                  print "A FAIRE\n";
                  }
               $buffer_fwd = sprintf "%-24s  IN A   %-15s ; %s\n", $hostname, $ip, $comment;
               $buffer_rev = sprintf "%3i    IN PTR %-15s\n", $ip_split[3], "$hostname.$dns_domain.";
               }

            else {
               $buffer_fwd = sprintf ";%-24s IN A   %-15s ; %s\n", $hostname, $ip, $comment;
               $buffer_rev = sprintf ";%3i   IN PTR %-15s\n", $ip_split[3], "$hostname.$dns_domain.";
               }
            print FILE_REVERSE_DNS $buffer_rev;
            print FILE_FORWARD_DNS $buffer_fwd;
            }
         close FILE_REVERSE_DNS;
         close FILE_FORWARD_DNS;
         print "- DNS: db.$sector_current.fwd db.$sector_current.rev [CREATE].\n" if $verbose;
         print "  Ex : sort -k 4n -t . $FOLDER_GEN_DNS/db.$sector_current.fwd\n"     if $verbose;
         }
      }
   }

#--------------------------------------------------------------------------------

sub shell_command {
   my $cmd = shift;

   require FileHandle;
   my $fh     = new FileHandle;
   my @result = ();
   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
   @result = <$fh>;
   close $fh;
   chomp @result;
   return @result;
   }

#--------------------------------------------------------------------------------

sub cmd_check_dns {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($opt_direct, $opt_reverse);

   GetOptions(
      'direct|d'  => \$opt_direct,
      'reverse|r' => \$opt_reverse,
      );

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   if ($opt_direct or not $opt_reverse) { # DDT to DNS check
      LOOP_ON_SECTOR:
      for my $sector_current (keys %{$computer_db}) {
         next if $sector_current eq 'dset';
         next if $sector_current eq 'pool';
         next if $sector_current eq 'pxe';
         next if $sector_current eq 'tag';
         next if $sector_current eq 'version';

         my @sectordb = @{$computer_db->{$sector_current}};

         LOOP_ON_COMPUTER:
         for my $computer (@sectordb) {
            my ($mac_address, $attribute) = %{$computer};
            #my $new_mac = normalize_mac_address($mac_address);
            my $ip = $attribute->{'ip'};
            next LOOP_ON_COMPUTER if not $ip =~ m/$DDT::RE::IPv4_ADDRESS/xms;
            next LOOP_ON_COMPUTER if $attribute->{'enabled'} eq 'no';

            my $dns_hostname_fq = scalar gethostbyaddr(inet_aton($ip), AF_INET);
            my ($dns_hostname) = split /\./, $dns_hostname_fq;

            if ($attribute->{'hostname'} ne $dns_hostname) {
               print "$mac_address ($sector_current) $ip - $dns_hostname / $attribute->{'hostname'} # $attribute->{'comment'}\n";
               next LOOP_ON_COMPUTER;
               }

            my $packed_ip = scalar gethostbyname($dns_hostname_fq);
            if (defined $packed_ip) {
               my $ip_address = inet_ntoa($packed_ip);
               if ($ip ne $ip_address) {
                  print "Error: bad IP for reverse DNS on $dns_hostname_fq / $ip\n";
                  next LOOP_ON_COMPUTER;
                  }
               }
            }
         }
      }

   if ($opt_reverse) {  # DNS to DDT check
      my %saw; # count for unique member
      my @dns_domain_list = sort grep !$saw{$_}++,
         map $computer_db->{'dset'}{$_}{'dns_extension'},
         grep exists($computer_db->{'dset'}{$_}{'dns_extension'}),
         keys $computer_db->{'dset'};
      LOOP_ON_DNS:
      for my $dns (@dns_domain_list) {
         LOOP_ON_IP:
         for (shell_command("host -t A -l $dns")) {
            # smtp2.legi.grenoble-inp.fr has address 194.254.67.37
            next if not m/has address/;
            next if not m/^(\w[\w-_\.]+\w)\s+has\saddress\s+(\d[\d\.]+\d)$/;
            my ($hostname_fq, $ip) = ($1, $2);
            control_syntax_ip($ip) or next LOOP_ON_IP;
            if (control_exist_ip($computer_db, $ip) == 1) {
               printf "Unkown IP: %-15s / %s\n", $ip, $hostname_fq;
               next LOOP_ON_IP;
               }
            }
         }
      }
   }

#-------------------------------------------------------------------------------
#Nom: load_data_dhcp
#Description: permet de charger le fichier de données YAML via les fichiers de configuration
#             machines.
#            ATTENTION: LES COMMENTAIRES DU FICHIER DISPARAITRONT.

sub load_data_dhcp {
   my ($sector, $input_file) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   my @T_mac;
   my @T_host;
   my @T_ip;
   my $cpt;
   open (FILE, "<$input_file");
   my @buffer = <FILE>;
   close(FILE);

   LINE:
   for my $ligne (@buffer) {
      #--
      $ligne =~ s/#.*$//;
      $ligne =~ s/\s+/ /;
      $ligne =~ s/^\s+//;
      next if $ligne eq '';

      if ($ligne =~ /^host /) {
         $cpt=0;
         my @T_split = split(/host\s+/, $ligne);
         @T_host = split(/ /, $T_split[1]);
         chomp($T_host[0]);

         $cpt++;
         }

      if ($ligne =~ /^*ethernet /) {
         $ligne =~ s/;//g;
         @T_mac = split(/ethernet\s+/, $ligne);
         chomp($T_mac[1]);
         $cpt++;
         }

      if ($ligne =~ /^*address /) {
         $ligne =~ s/;//g;
         @T_ip = split(/address\s+/, $ligne);
         chomp($T_ip[1]);

         $cpt++;
         }

      if ($cpt == 3) {
         #   print "MAC $T_mac[1] HOST $T_host[0] IP $T_ip[1].\n";
         my $mac = $T_mac[1];
         my $hostname = $T_host[0];
         my $ip = $T_ip[1];
         $cpt = 0;

         if ( control_exist_hostname($computer_db, $sector, $hostname) == 0 ) {
            print "Error: host already exist in sector $sector: $hostname\n";
            next LINE;
            }
         control_syntax_mac_address($mac) or next LINE;
         if ( control_exist_mac($computer_db, $mac) == 0) {
            print "Error: physical MAC address already exists: $mac\n";
            next LINE;
            }

         control_syntax_ip($ip) or next LINE;
         if ( control_exist_ip($computer_db, $ip) == 0 ) {
            print "Error: IP address already exists: $ip\n";
            next LINE;
            }
         my $timestamp = time;
         push @{$computer_db->{$sector}}, { $mac => {
            'hostname'     => $hostname,
            'ip'           => $ip,
            'address_type' => 'dhcp',
            'enabled'      => 'yes',
            'create_time'  => $timestamp,
            'modify_time'  => $timestamp,
            'alias'        =>  '',
            }};
         }
      }
   }

#-------------------------------------------------------------------------------
#Nom: load_data_pool
#Description: permet de charger le fichier YAML via les fichiers de conf 'pool' du dhcp.

sub load_data_pool {
   my ($sector, $input_file) = @_;

   my @T_mac;

   open (FILE, "<$input_file");
   my @buffer = <FILE>;
   close(FILE);

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   for my $ligne (@buffer) {
      #--
      $ligne =~ s/#.*$//;
      $ligne =~ s/\s+/ /;
      $ligne =~ s/^\s+//;
      $ligne =~ s/;//g;
      $ligne =~ s/"//g;
      next if $ligne eq '';

      if (($ligne =~ /^subclass/)) {
         my @T_split = split(/ / ,$ligne);
         my $pool = $T_split[1];

         @T_mac = split(/:/ , $T_split[2]);
         my $mac = $T_mac[1].":".$T_mac[2].":".$T_mac[3].":".$T_mac[4].":".$T_mac[5].":".$T_mac[6];
         control_syntax_mac_address($mac) or next;
         if (control_exist_mac($computer_db, $mac) == 0) {
            print "Error: physical MAC address already exists: $mac\n";
            next;
            }

         #--- cette partie teste si le pool existe.
         if (not exists $computer_db->{'pool'}{$pool}) {
            print "Error: create pool with create_pool command before load database: $pool\n";
            exit;
            }

         if ($computer_db->{'pool'}{'domain'} eq $sector) {
            my $timestamp = time;
            push @{$computer_db->{$sector}}, { $mac => {
               'hostname'     => $pool,
               'ip'           => $pool,
               'address_type' => 'pool-dhcp',
               'enabled'      => 'yes',
               'create_time'  => $timestamp,
               'modify_time'  => $timestamp,
               }};
            }
         else {
            print "Ajout de la machine $mac [FAILED]\n";
            print "Error: the pool doesn't exists: $pool, for the domain: $sector\n";
            }
         }
      }
   }

#-------------------------------------------------------------------------------

sub load_data_file {
   my ($sector, $input_file, $type_file) = @_;

   my $computer_db = ipamdb_load($COMPUTER_YAML);

   #$computer_db
   if ($type_file eq 'dhcp') {
      load_data_dhcp($sector, $input_file);
      }

   elsif ($type_file eq 'pool-dhcp') {
      load_data_pool($sector, $input_file);
      }

   ipamdb_save("$COMPUTER_YAML", $computer_db);
   }

#-------------------------------------------------------------------------------

sub cmd_load_database {
   local @ARGV = @_;

   my $help = get_cmd_name();
   my ($sector, $input_file, $type_file);

   GetOptions(
      'sector|s|d=s'    => \$sector,
      'filename|f=s'    => \$input_file,
      'kind|k=s'        => \$type_file,
      );

   exit_on_error_option($help)
      if $sector  eq ''
      or $input_file eq ''
      or $type_file  eq '';

   load_data_file($sector, $input_file, $type_file);
   }

#-------------------------------------------------------------------------------
#Nom: backup_database
#Description: sauvegarde et réinitialise les fichiers d'enregistrements DHCP.

sub backup_database {
   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime time;
   $year += 1900;
   $mon++;
   my $date = sprintf '%04i-%02i-%02i-%02i-%02i-%02i', $year, $mon, $mday, $hour, $min, $sec;

   copy($COMPUTER_YAML, "$FOLDER_BACKUP/$COMPUTER_BASENAME-$date.conf") or die "Error: database copy backup failed: $!\n";
   }

#-------------------------------------------------------------------------------
# HELP section
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#Nom: exit_on_error_option
#Description: messages d'aide des options pour les différentes commandes

sub exit_on_error_option {
  my ($command) = @_;

   if ($command eq 'add-dhcp') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
      print " -h : computer hostname (mandatory if option -i != 'pool'). Example: -h info8pc154\n";
      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A\n";
      print " -i : internet IP address (mandatory). Possible value: classical IP address or the keyword 'pool'\n";
      print " -p : name of the DHCP pool to which the machine belongs (mandatory if option -i == 'pool')\n";
      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
      print "Example:\n";
      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";
      }

   elsif ($command eq 'add-float') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory)\n";
      print " -p : name of the DHCP pool to which the machine belongs (mandatory)\n";
      print " -m : physical MAC address (mandatory)\n";
      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
      print "Example:\n";
      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";
      }

   elsif ($command eq 'add-static') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory)\n";
      print " -i : internet IP address (mandatory)\n";
      print " -h : computer hostname (mandatory)\n";
      print " -m : physical MAC address (mandatory)\n";
      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
      print "Example:\n";
      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";
      }

   elsif ($command eq 'add-virtual') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory)\n";
      print " -i : internet IP address (mandatory)\n";
      print " -h : computer hostname (mandatory)\n";
      print " -c : comment (mandatory). Example: 2014-04-07 DELL Laptop 6400 - Olivier Toto (INFO)\n";
      print "Example:\n";
      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";
      }

   elsif ($command eq 'add-alias') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory)\n";
      print " -h : computer hostname (mandatory)\n";
      print " -a : computer alias name (mandatory)\n";
      }

   elsif ($command eq 'create-sector') {
      print "List of options for command: $command\n";
      print " -s : new sector (mandatory)\n";
      print " -e : DNS domain name extension( mandatory). Example legi.grenoble-inp.fr\n";
      print " -c : comment (mandatory). Example: 2016-08-22 VLAN legi-261 (INFO)\n";
      print "Examples:\n";
      print " ddt create-sector -s legi-sector03 -e legi.grenoble-inp.fr -c '2016-08-22 VLAN legi-261 (INFO)'\n";
      }

   elsif ($command eq 'create-pool') {
      print "List of options for command: $command\n";
      print " -p : name of the DHCP pool. Example: pool-legi-priv\n";
      print " -s : sector attachment for the pool. (sector attachment must exist in file $COMPUTER_BASENAME.conf). Example: legi-sector03\n";
      print " -f : configuration filename on the DHCP server for the pool\n";
      print " -i : adresse(s) IP ou plage d'IP. Séparateur d'adresses IP: ','. Séparateur de plage '-'\n";
      print "Examples:\n";
      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";
      print " ddt create-pool -p legi-pool2 -s legi-sector03 -f legi-pool-public  -i 192.168.10.1-192.168.10.4\n";
      }

   elsif ($command eq 'create-pxe') {
      print "List of options for command: $command\n";
      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
      print " -n : internet IP address for the DHCP next-server.\n";
      print " -f : filename on TFTP server to load at boot\n";
      print " -c : comment (mandatory). Example: 2014-04-07 PXE Boot for CentOS (MOST)\n";
      }

   elsif ($command eq 'remove-pxe') {
      print "List of options for command: $command\n";
      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
      }

   elsif ($command eq 'enable-pxe') {
      print "List of options for command: $command\n";
      print " -h : computer hostname (mandatory unless option -i)\n";
      print " -i : internet IP address (mandatory unless option -h)\n";
      print " -s : sector attachment (mandatory if option -h)\n";
      print " -b : name of the PXE/BOOTP configuration. Example: most\n";
      }

   elsif ($command eq 'disable-pxe') {
      print "List of options for command: $command\n";
      print " -h : computer hostname (mandatory unless option -i)\n";
      print " -i : internet IP address (mandatory unless option -h)\n";
      print " -s : sector attachment (mandatory if option -h)\n";
      }

   elsif ($command eq 'create-tag') {
      print "List of options for command: $command\n";
      print " -t : name of the TAG (mandatory). Example: restricted\n";
      print " -c : comment (mandatory). Example: 2014-04-07 tag restricted (INFO)\n";
      print "tag 'universal' is intrinsic\n";
      }

   elsif ($command eq 'remove-tag') {
      print "List of options for command: $command\n";
      print " -b : name of the TAG. Example: restricted\n";
      }

   elsif ($command eq 'change-mac') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
      print " -h : computer hostname (mandatory unless option -i)\n";
      print " -i : internet IP address (mandatory unless option -h). Possible value: classical IP address or the keyword 'pool'\n";
      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
      }

   elsif ($command eq 'change-ip') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
      print " -h : computer hostname (mandatory)\n";
      print " -i : new internet IP address (mandatory). Possible value: classical IP address\n";
      }

   elsif ($command eq 'change-host') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
      print " -i : internet IP address (mandatory). Possible value: classical IP address\n";
      print " -h : new computer hostname (mandatory)\n";
      print "It's not possible to change hostname for computer that belongs to a pool\n";
      }

   elsif ($command eq 'change-comment') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
      print " -c : new comment (mandatory)\n";
      }

   elsif ($command eq 'change-sector') {
      print "List of options for command: $command\n";
      print " -s : new sector attachment (mandatory). Example: -s legi-sector03\n";
      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
      print " -i : internet IP address (mandatory)\n";
      }

   elsif ($command eq 'change-tag') {
      print "List of options for command: $command\n";
      print " -h : computer hostname (mandatory unless option -i or -m)\n";
      print " -s : sector attachment (mandatory). Example: -s legi-sector03\n";
      print " -i : internet IP address (mandatory unless option -h or -m)\n";
      print " -m : physical MAC address (mandatory unless option -h or -i, priority). Example: -m 0F:58:AB:2A:22:11\n";
      print " -t : list of tags separated by comma (mandatory). Example: -t internal,windows\n";
      }

   elsif ($command eq 'load-database') {
      print "List of options for command: $command\n";
      print " -s : sector attachment\n";
      print " -f : input file in DHCP format\n";
      print " -k : possible cases (kind): dhcp, pool-dhcp, fix-address\n";
      }

   elsif ($command eq 'enable-pc') {
      print "List of options for command: $command\n";
      print " -h : computer hostname (mandatory unless option -i)\n";
      print " -i : internet IP address (mandatory unless option -h)\n";
      print " -s : sector attachment (mandatory if option -h)\n";
      print "Examples:\n";
      print " ddt enable-pc -i 192.168.10.1\n";
      print " ddt enable-pc -s legi-sector03 -h kevinpc\n";
      }

   elsif ($command eq 'enable-float') {
      print "List of options for command: $command\n";
      print " -m : physical MAC address (mandatory)\n";
      print " -p : name of the DHCP pool (mandatory)\n";
      }

   elsif ($command eq 'disable-float') {
      print "List of options for command: $command\n";
      print " -m : physical MAC address (mandatory)\n";
      print " -p : name of the DHCP pool (mandatory)\n";
      }

   elsif ($command eq 'disable-pc') {
      print "List of options for command: $command\n";
      print " -h : computer hostname (mandatory unless option -i)\n";
      print " -i : internet IP address (mandatory unless option -h)\n";
      print " -s : sector attachment (mandatory if option -h)\n";
      print "Examples:\n";
      print " ddt disable-pc -i 192.168.10.1\n";
      print " ddt disable-pc -s legi-sector03 -h kevinpc\n";
      }

   elsif ($command eq 'del-pc') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory)\n";
      print " -h : computer hostname (mandatory unless option -i)\n";
      print " -i : internet IP address (mandatory unless option -h)\n";
      }

   elsif ($command eq 'del-float') {
      print "List of options for command: $command\n";
      print " -m : physical MAC address (mandatory)l\n";
      print " -p : name of the DHCP pool\n";
      }

   elsif ($command eq 'search-mac') {
      print "List of options for command: $command\n";
      print " -m : physical MAC address (mandatory). Example: -m 0F:58:AB:2A:22:11\n";
      }

   elsif ($command eq 'sector-add-ip') {
      print "List of options for command: $command\n";
      print " -s : sector attachment (mandatory)\n";
      print " -i : internet IP range address in CIDR notation (mandatory)\n";
      }

   else {
      print "No help for command: $command\n";
      }
   exit;
   }

#-------------------------------------------------------------------------------

sub cmd_version {

   print <<'END';
ddt - management of computer names and IP addresses
Copyright (C) 2006-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
Main author Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
License GNU GPL version 2 or later and Perl equivalent
END

   print "Database Version 1\n";
   print "Version $VERSION\n\n";
   print ' $Id: ddt 357 2018-09-14 09:58:01Z g7moreau $'."\n";
   return;
   }

#-------------------------------------------------------------------------------
#Nom: usage
#Description: message d'aide sur les commandes du script

sub cmd_help {
   print <<END;
ddt - management of computer names and IP addresses

 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]
 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]
 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]
 ddt check-dns [--direct] [--reverse]
 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]
 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]
 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]
 ddt create-tag [--tag|-t tag] [--comment|-c comment]
 ddt del-float [--pool|-p pool] [--mac|-m mac]
 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt disable-float [--pool|-p pool] [--mac|-m mac]
 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt enable-float [--pool|-p pool] [--mac|-m mac]
 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]
 ddt gen-dhcp-file
 ddt gen-dns-file [--verbose]
 ddt help
 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]
 ddt remove-pxe [--bootp|-b pxe_config]
 ddt remove-tag [--tag|-t tag]
 ddt search-mac [--mac|-m mac]
 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr] 
 ddt show-sector [--no-header|-H]
 ddt show
 ddt show-pool [--no-header|-H]
 ddt show-pxe [--no-header|-H]
 ddt show-tag [--no-header|-H]
 ddt version

COMMANDS

 * add-alias         : add an alias for a computer (like CNAME for the DNS)
 * add-dhcp          : add a computer with a fix DHCP IP or in a DHCP pool
 * add-float         : add a computer with an IP in a DHCP pool
 * add-static        : add a computer with a static IP
 * add-virtual       : add a virtual computer with a static IP but a virtual MAC (useful to declare float computer in DNS)
 * change-comment    : change the computer comment
 * change-sector     : change the sector attachment for a computer
 * change-host       : change the computer hostname
 * change-ip         : change the computer IP address
 * change-mac        : change the computer physical MAC address
 * change-tag        : change the list of TAGs associated to a computer
 * check-dns         : check the DNS table for base IPs
 * create-sector     : create a new sector
 * create-pool       : create a new pool for DHCP records
 * create-pxe        : create a new PXE/BOOTP configuration
 * create-tag        : create a new TAG
 * del-float         : remove a computer from a DHCP pool
 * del-pc            : remove a computer (DHCP or static IP) from the YAML database
 * disable-pc        : disable a computer (DHCP and/or DNS) (but keep it in the database)
 * disable-float     : disable a computer from a DHCP pool (but keep it in the database)
 * disable-pxe       : remove PXE/BOOTP configuration on a computer
 * enable-float      : enable a previous disable computer (DHCP and/or DNS)
 * enable-pc         : enable a previous disable computer (DHCP and/or DNS)
 * enable-pxe        : enable PXE/BOOTP configuration on a computer
 * gen-dhcp-file     : generate DHCP files for the isc DHCP server
 * gen-dns-file      : generate DNS files for the bind domain server
 * help              : this help
 * load-database     : load the YAML database (be careful)
 * remove-pxe        : remove a PXE/BOOTP configuration
 * remove-tag        : remove a TAG
 * search-mac        : search physical MAC address computer
 * ddt sector-add-ip : add IP range check on a sector
 * show-sector       : list all sector group of computer
 * show              : list all computers
 * show-pool         : list all pool
 * show-pxe          : list PXE/BOOTP configuration
 * show-tag          : list all TAGs
 * version           : return program version
END
   return;
   }

################################################################
# documentation
################################################################

__END__

=head1 NAME

ddt - management of computer names and IP addresses


=head1 USAGE

 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]
 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]
 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]
 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]
 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]
 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]
 ddt check-dns [--direct] [--reverse]
 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]
 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]
 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]
 ddt create-tag [--tag|-t tag] [--comment|-c comment]
 ddt del-float [--pool|-p pool] [--mac|-m mac]
 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt disable-float [--pool|-p pool] [--mac|-m mac]
 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt enable-float [--pool|-p pool] [--mac|-m mac]
 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]
 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]
 ddt gen-dhcp-file
 ddt gen-dns-file [--verbose]
 ddt help
 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]
 ddt remove-pxe [--bootp|-b pxe_config]
 ddt remove-tag [--tag|-t tag]
 ddt search-mac [--mac|-m mac]
 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr] 
 ddt show-sector [--no-header|-H]
 ddt show
 ddt show-pool [--no-header|-H]
 ddt show-pxe [--no-header|-H]
 ddt show-tag [--no-header|-H]
 ddt version


=head1 DESCRIPTION

DDT is an acronym for DHCP-DNS-Tools.
The previous command name was not C<ddt> but just C<dhcp-dns-tools>...
In practise, DDT is an IP Address Management (IPAM) service.
It has been used in the LEGI laboratory for over 10 years.

The tool is quite effective and tries to keep things simple
but easily configurable for your site like a swiss army knife.
Everything is saved in a YAML database
and entries could be added, deleted, or modified by the command line.


=head1 COMMANDS

=head2 add-alias

 ddt add-alias [--hostname|-h hostname] [--sector|-s|-d sector] [--alias|-a alias]

=head2 add-dhcp

 ddt add-dhcp [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]

=head2 add-float

 ddt add-float [--pool|-p pool] [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]

=head2 add-static

 ddt add-static [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--comment|-c comment]

=head2 add-virtual

 ddt add-virtual [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--comment|-c comment]

=head2 change-comment

 ddt change-comment [--sector|-s|-d sector] [--mac|-m mac] [--comment|-c comment]

=head2 change-sector

 ddt change-sector [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]

=head2 change-host

 ddt change-host [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]

=head2 change-ip

 ddt change-ip [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]

=head2 change-mac

 ddt change-mac [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac]

=head2 change-tag

 ddt change-tag [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--mac|-m mac] [--tag|-t tag]

=head2 check-dns

 ddt check-dns [--direct] [--reverse]

=head2 create-sector

 ddt create-sector [--sector|-s|-d sector] [--dns-extension|-e dns_extension] [--comment|-c comment]

=head2 create-pool

 ddt create-pool [--pool|-p pool] [--sector|-s|-d sector] [--file-pool|-f file_pool] [--ipaddress-pool|-i ipaddress_pool]

=head2 create-pxe

 ddt create-pxe [--bootp|-b pxe_config] [--next-server|-n next_server] [--filename|-f filename] [--comment|-c comment]

=head2 create-tag

 ddt create-tag [--tag|-t tag] [--comment|-c comment]

=head2 del-float

 ddt del-float [--pool|-p pool] [--mac|-m mac]

=head2 del-pc

 ddt del-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]

=head2 disable-pc

 ddt disable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]

=head2 disable-float

 ddt disable-float [--pool|-p pool] [--mac|-m mac]

=head2 disable-pxe

 ddt disable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]

=head2 enable-float

 ddt enable-float [--pool|-p pool] [--mac|-m mac]

=head2 enable-pc

 ddt enable-pc [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip]

=head2 enable-pxe

 ddt enable-pxe [--hostname|-h hostname] [--sector|-s|-d sector] [--ip|-i ip] [--bootp|-b pxe_config]

=head2 gen-dhcp-file

 ddt gen-dhcp-file

=head2 gen-dns-file

 ddt gen-dns-file [--verbose]

=head2 help

 ddt help

=head2 load-database

 ddt load-database [--sector|-s|-d sector] [--filename|-f filename] [--kind|-k kind]

=head2 remove-pxe

 ddt remove-pxe [--bootp|-b pxe_config]

=head2 remove-tag

 ddt remove-tag [--tag|-t tag]

=head2 search-mac

 ddt search-mac [--mac|-m mac]

=head2 sector-add-ip

 ddt sector-add-ip [--sector|-s|-d sector] [--ip-range|-i ip_cidr] 

=head2 show-sector

 ddt show-sector [--no-header|-H]

=head2 show

 ddt show

=head2 show-pool

 ddt show-pool [--no-header|-H]

=head2 show-pxe

 ddt show-pxe [--no-header|-H]

=head2 show-tag

 ddt show-tag [--no-header|-H]

=head2 version

 ddt version


=head1 AUTHORS

Written by Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>, Kevin Reverchon, Olivier De-Marchi - Grenoble - France


=head1 LICENSE AND COPYRIGHT

License GNU GPL version 2 or later and Perl equivalent

Copyright (C) 2006-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
