source: trunk/klask @ 74

Last change on this file since 74 was 74, checked in by g7moreau, 14 years ago
  • Add cleandb command to delete duplicate entry when time date are different
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 61.4 KB
RevLine 
[2]1#!/usr/bin/perl -w
[32]2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 74 2010-11-08 09:50:46Z g7moreau $
[2]6
7use strict;
[3]8use warnings;
[63]9use version; our $VERSION = qv('0.5.5');
[2]10
[63]11use Readonly;
[64]12use FileHandle;
[2]13use Net::SNMP;
[44]14#use YAML;
15use YAML::Syck;
[2]16use Net::Netmask;
17use Net::CIDR::Lite;
18use NetAddr::IP;
[69]19use Getopt::Long qw(GetOptions GetOptionsFromArray);;
[68]20use Socket;
[2]21
[8]22# apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
[68]23# libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl
[23]24# arping fping bind9-host arpwatch
[2]25
[26]26my $KLASK_VAR      = '/var/cache/klask';
[13]27my $KLASK_CFG_FILE = '/etc/klask.conf';
[28]28my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
29my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
[2]30
[26]31test_running_environnement();
32
[44]33my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
[2]34
[63]35my %DEFAULT = %{ $KLASK_CFG->{default} };
36my @SWITCH  = @{ $KLASK_CFG->{switch}  };
[2]37
[11]38my %switch_level = ();
[63]39my %SWITCH_DB    = ();
[2]40LEVEL_OF_EACH_SWITCH:
[11]41for my $sw (@SWITCH){
42   $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
[25]43   $SWITCH_DB{$sw->{hostname}} = $sw;
[2]44   }
[63]45@SWITCH = reverse sort { $switch_level{$a->{hostname}} <=> $switch_level{$b->{hostname}} } @{$KLASK_CFG->{switch}};
[2]46
[15]47my %SWITCH_PORT_COUNT = ();
[2]48
[11]49my %CMD_DB = (
[2]50   help       => \&cmd_help,
[36]51   version    => \&cmd_version,
[2]52   exportdb   => \&cmd_exportdb,
53   updatedb   => \&cmd_updatedb,
54   searchdb   => \&cmd_searchdb,
[4]55   removedb   => \&cmd_removedb,
[74]56   cleandb   => \&cmd_cleandb,
[2]57   search     => \&cmd_search,
58   enable     => \&cmd_enable,
59   disable    => \&cmd_disable,
60   status     => \&cmd_status,
[4]61   updatesw   => \&cmd_updatesw,
62   exportsw   => \&cmd_exportsw,
[2]63   iplocation => \&cmd_iplocation,
[69]64   'ip-free'  => \&cmd_ip_free,
[35]65   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
[2]66   );
67
[63]68Readonly my %INTERNAL_PORT_MAP => (
[2]69   0 => 'A',
70   1 => 'B',
71   2 => 'C',
72   3 => 'D',
73   4 => 'E',
74   5 => 'F',
75   6 => 'G',
76   7 => 'H',
77   );
[63]78Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP;
[2]79
[63]80Readonly my %SWITCH_KIND => (
[20]81   J3299A => { model => 'HP224M',     match => 'HP J3299A ProCurve Switch 224M'  },
82   J4120A => { model => 'HP1600M',    match => 'HP J4120A ProCurve Switch 1600M' },
[27]83   J9029A => { model => 'HP1800-8G',  match => 'PROCURVE J9029A'                 },
[68]84   J9449A => { model => 'HP1810-8G',  match => 'HP ProCurve 1810G - 8 GE'        },
[20]85   J4093A => { model => 'HP2424M',    match => 'HP J4093A ProCurve Switch 2424M' },
86   J4813A => { model => 'HP2524',     match => 'HP J4813A ProCurve Switch 2524'  },
[21]87   J4900A => { model => 'HP2626A',    match => 'HP J4900A ProCurve Switch 2626'  },
[42]88   J4900B => { model => 'HP2626B',    match => 'J4900B.+?Switch 2626'            },# ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626
[30]89   J4899B => { model => 'HP2650',     match => 'ProCurve J4899B Switch 2650'     },
[20]90   J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G' },
[41]91   J9022A => { model => 'HP2810-48G', match => 'ProCurve J9022A Switch 2810-48G' },
[20]92   J4903A => { model => 'HP2824',     match => 'J4903A.+?Switch 2824,'           },
93   J4110A => { model => 'HP8000M',    match => 'HP J4110A ProCurve Switch 8000M' },
94   BS350T => { model => 'BS350T',     match => 'BayStack 350T HW'                },
[18]95   );
[63]96
97Readonly my %OID_NUMBER => (
[68]98   sysDescription  => '1.3.6.1.2.1.1.1.0',
99   sysName         => '1.3.6.1.2.1.1.5.0',
100   sysContact      => '1.3.6.1.2.1.1.4.0',
101   sysLocation     => '1.3.6.1.2.1.1.6.0',
[19]102   );
[18]103
[63]104Readonly my $RE_MAC_ADDRESS  => qr{ [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} }xms;
105Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
106
107
[2]108################
109# principal
110################
111
[11]112my $cmd = shift @ARGV || 'help';
113if (defined $CMD_DB{$cmd}) {
114   $CMD_DB{$cmd}->(@ARGV);
[2]115   }
116else {
[63]117   print {*STDERR} "klask: command $cmd not found\n\n";
[11]118   $CMD_DB{help}->();
[2]119   exit 1;
120   }
121
122exit;
123
[26]124sub test_running_environnement {
125   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
126   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
[63]127   return;
[26]128   }
129
[34]130sub test_switchdb_environnement {
131   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
[63]132   return;
[34]133   }
134
135sub test_maindb_environnement {
136   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
[63]137   return;
[34]138   }
139
[2]140###
141# fast ping dont l'objectif est de remplir la table arp de la machine
142sub fastping {
[68]143   # Launch this command without waiting...
144   system "fping -c 1 @_ >/dev/null 2>&1 &";
[63]145   return;
[2]146   }
147
[63]148sub shell_command {
149   my $cmd = shift;
150
[64]151   my $fh     = new FileHandle;
152   my $result = '';
[63]153   open $fh, q{-|}, "$cmd" or die "Can't exec $cmd\n";
[64]154   $result .= <$fh>;
[63]155   close $fh;
156   chomp $result;
157   return $result;
158   }
159
[2]160###
161# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
162sub resolve_ip_arp_host {
163   my $param_ip_or_host = shift;
[63]164   my $interface = shift || q{*};
165   my $type      = shift || q{fast};
[2]166
167   my %ret = (
168      hostname_fq  => 'unknow',
169      ipv4_address => '0.0.0.0',
170      mac_address  => 'unknow',
171      );
172
173#   my $cmdarping  = `arping -c 1 -w 1 -rR $param 2>/dev/null`;
[68]174#   if ( not $param_ip_or_host =~ m/^\d+ \. \d+ \. \d+ \. \d+$/xms ) {
175#      $param_ip_or_host =~ s/ \. .* //xms;
176#      }
[2]177
[68]178   #my $dns_resolver =  Net::DNS::Resolver->new;
179   #my $dns_answer = $dns_resolver->query($param_ip_or_host);
180   #if ($dns_answer) {
181   #   $ret{ipv4_address} = $dns_answer->address;
182   #   }
183
184   # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))'
185   my $packed_ip = scalar gethostbyname($param_ip_or_host);
186   return %ret if not defined $packed_ip;
187   $ret{ipv4_address} = inet_ntoa($packed_ip);
188
189   # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)'
190   my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET);
191#   return %ret if not defined $hostname_fq;
192   $ret{hostname_fq} = $hostname_fq if defined $hostname_fq;
193
194#print "DEBUG : $param_ip_or_host --  $ret{ipv4_address} --   $hostname_fq \n";
195
[2]196   # controler que arpwatch tourne !
197   # resultat de la commande arpwatch
198   # /var/lib/arpwatch/arp.dat
199   # 0:13:d3:e1:92:d0        192.168.24.109  1163681980      theo8sv109
[44]200   # my $cmd = "grep  -e '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/arp.dat | sort +2rn | head -1";
201   # my $cmd = "grep  -he '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/*.dat | sort +2rn | head -1";
[68]202
203### BOGUE ICI
204### FAIRE LE GREP SUR L'IP !
205
206   # my $cmd = q{grep  -he '\b} . $param_ip_or_host . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
207   my $cmd = q{grep  -he '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
[62]208   my $cmd_arpwatch = shell_command $cmd;
209   my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
[44]210
[68]211#   $ret{ipv4_address} = $ip        if $ip;
[2]212   $ret{mac_address}  = $arp       if $arp;
213   $ret{timestamp}    = $timestamp if $timestamp;
214
[63]215   my $nowtimestamp = time;
[3]216
217   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 3 * 3600 ) ) ) {
218      $ret{mac_address} = 'unknow';
219      return %ret;
220      }
221
[63]222   # resultat de la commande arp
[2]223   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
[44]224   # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37
[63]225   my $cmd_arp  = shell_command "arp -a $param_ip_or_host";
226   if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) {
227      ( $ret{hostname_fq}, $ret{ipv4_address}, $ret{mac_address} )  = ($1, $2, $3);
228      }
[2]229
[63]230   # resultat de la commande host si le parametre est ip
231   # 250.66.254.194.in-addr.arpa domain name pointer legihp2100.hmg.inpg.fr.
[68]232#   my $cmd_host = shell_command "host $param_ip_or_host";
233#   if ( $cmd_host =~ m/domain \s name \s pointer \s (\S+) \.$/xms ) {
234#      $ret{hostname_fq} = $1;
235#      }
[2]236
[63]237   # resultat de la commande host si parametre est hostname
238   # tech7meylan.hmg.inpg.fr has address 194.254.66.240
[68]239#   if ( $cmd_host =~ m/(\S*) \s has \s address \s ( $RE_IPv4_ADDRESS )$/xms ) {
240#      ( $ret{hostname_fq}, $ret{ipv4_address} ) = ($1, $2);
241#      }
[2]242
[65]243   # Connerie !
244   # Inverse les IP !!
245   # if ( $cmd_host =~ m/ \b ( $RE_IPv4_ADDRESS ) \. in-addr \. arpa \s/xms ) {
246   #    $ret{ipv4_address} = $1;
247   #    }
[63]248   #$ret{hostname_fq}  = $param_ip_or_host if not defined $1 and $ret{hostname_fq} eq 'unknow';
[2]249
[63]250   if ($ret{mac_address} ne 'unknow') {
[2]251      my @paquets = ();
[63]252      foreach ( split m/ : /xms, $ret{mac_address} ) {
253         my @chars = split m//xms, uc "00$_";
[2]254         push @paquets, "$chars[-2]$chars[-1]";
255         }
[63]256      $ret{mac_address} = join q{:}, @paquets;
[2]257      }
258
259   return %ret;
260   }
261
[20]262# Find Surname of a switch
263sub get_switch_model {
[22]264   my $sw_snmp_description = shift || 'unknow';
[63]265
[20]266   for my $sw_kind (keys %SWITCH_KIND) {
[64]267      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ?
[63]268
[20]269      return $SWITCH_KIND{$sw_kind}->{model};
270      }
[63]271
[22]272   return $sw_snmp_description;
[20]273   }
274
[2]275###
276# va rechercher le nom des switchs pour savoir qui est qui
[4]277sub init_switch_names {
[2]278   my $verbose = shift;
[63]279
[14]280   printf "%-25s                %-25s %s\n",'Switch','Description','Type';
281   print "-------------------------------------------------------------------------\n" if $verbose;
[2]282
283   INIT_EACH_SWITCH:
[11]284   for my $sw (@SWITCH) {
[3]285      my %session = ( -hostname   => $sw->{hostname} );
286         $session{-version} = $sw->{version}   || 1;
[11]287         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
[63]288         if (exists $sw->{version} and $sw->{version} eq '3') {
[3]289            $session{-username} = $sw->{username} || 'snmpadmin';
290            }
291         else {
[11]292            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
[3]293            }
294
295      $sw->{local_session} = \%session;
296
297      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[14]298      print "$error \n" if $error;
299
[2]300      my $result = $session->get_request(
[18]301         -varbindlist => [
[63]302            $OID_NUMBER{sysDescription},
[18]303            $OID_NUMBER{sysName},
304            $OID_NUMBER{sysContact},
305            $OID_NUMBER{sysLocation},
306            ]
[2]307         );
[18]308      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
[63]309      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescription}});
[3]310      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
311      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
[2]312      $session->close;
[22]313
[63]314      # Ligne à virer car on récupère maintenant le modèle du switch
315      my ($desc, $type) = split m/ : /xms, $sw->{description}, 2;
[20]316      printf "%-25s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
[2]317      }
318
319   print "\n" if $verbose;
[63]320   return;
[2]321   }
322
323###
324# convertit l'hexa (uniquement 2 chiffres) en decimal
[4]325sub hex_to_dec {
[2]326   #00:0F:1F:43:E4:2B
[63]327   my $car = '00' . uc shift;
[2]328
[4]329   return '00' if $car eq '00UNKNOW';
[2]330   my %table = (
[62]331      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
332      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
[63]333      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
[2]334      );
[63]335   my @chars = split m//xms, $car;
[2]336   return $table{$chars[-2]}*16 + $table{$chars[-1]};
337   }
338
339###
340# convertit l'@ arp en decimal
[4]341sub arp_hex_to_dec {
[2]342   #00:0F:1F:43:E4:2B
343   my $arp = shift;
344
[63]345   my @paquets = split m/ : /xms, $arp;
346   my $return = q{};
[2]347   foreach(@paquets) {
[63]348      $return .= q{.} . hex_to_dec($_);
[2]349      }
350   return $return;
351   }
352
353###
354# va rechercher le port et le switch sur lequel est la machine
355sub find_switch_port {
[63]356   my $arp             = shift;
357   my $switch_proposal = shift || q{};
358
[2]359   my %ret;
[63]360   $ret{switch_description} = 'unknow';
361   $ret{switch_port} = '0';
[2]362
363   return %ret if $arp eq 'unknow';;
364
[22]365   my @switch_search = @SWITCH;
[63]366   if ($switch_proposal ne q{}) {
[11]367      for my $sw (@SWITCH) {
[2]368         next if $sw->{hostname} ne $switch_proposal;
[22]369         unshift @switch_search, $sw;
[2]370         last;
371         }
372      }
373
[63]374   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($arp);
375
[2]376   LOOP_ON_SWITCH:
[22]377   for my $sw (@switch_search) {
[3]378      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[22]379      print "$error \n" if $error;
380
[2]381      my $result = $session->get_request(
382         -varbindlist => [$research]
383         );
[63]384      if (not defined $result or $result->{$research} eq 'noSuchInstance') {
[2]385         $session->close;
386         next LOOP_ON_SWITCH;
387         }
388
389         my $swport = $result->{$research};
390         $session->close;
391
392         # IMPORTANT !!
[63]393         # ceci empeche la detection sur certains port ...
[2]394         # en effet les switch sont relies entre eux par un cable reseau et du coup
395         # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
396         # cette partie est a ameliore, voir a configurer dans l'entete
397         # 21->24 45->48
398#         my $flag = 0;
399         SWITCH_PORT_IGNORE:
400         foreach my $p (@{$sw->{portignore}}) {
[22]401            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
[2]402#            $flag = 1;
403            next LOOP_ON_SWITCH;
404            }
405#         if ($flag == 0) {
406            $ret{switch_hostname}    = $sw->{hostname};
407            $ret{switch_description} = $sw->{description};
[22]408            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
[63]409
[2]410            last LOOP_ON_SWITCH;
411#            }
412#         }
413#      $session->close;
414      }
415   return %ret;
416   }
417
418###
419# va rechercher les port et les switch sur lequel est la machine
420sub find_all_switch_port {
421   my $arp = shift;
422
423   my $ret = {};
424
425   return $ret if $arp eq 'unknow';
426
[11]427   for my $sw (@SWITCH) {
[15]428      $SWITCH_PORT_COUNT{$sw->{hostname}} = {} if not exists $SWITCH_PORT_COUNT{$sw->{hostname}};
[2]429      }
430
[63]431   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($arp);
[2]432   LOOP_ON_ALL_SWITCH:
[11]433   for my $sw (@SWITCH) {
[3]434      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
[13]435      print "$error \n" if $error;
436
[2]437      my $result = $session->get_request(
438         -varbindlist => [$research]
439         );
[13]440
[63]441      if(defined $result and $result->{$research} ne 'noSuchInstance'){
[2]442         my $swport = $result->{$research};
443
[68]444#print "DEBUG $arp  $swport --  $sw->{hostname} \n";
445         if ( $sw->{hostname} eq 'sw10-batE1-3s.hmg.priv' and $swport == 19 ) { $swport = 20; print "DEBUG $swport --  $sw->{hostname} \n";}
446         if ( $sw->{hostname} eq 'sw8-batE1-3s.hmg.priv'  and $swport == 23 ) { $swport = 24; print "DEBUG $swport --  $sw->{hostname} \n";}
447#         if ( $sw->{hostname} eq 'sw10-batE1-3s.hmg.priv' ) {  print "DEBUG $swport --  $sw->{hostname} \n";}
448#         if ( $sw->{hostname} eq 'sw8-batE1-3s.hmg.priv'  ) {  print "DEBUG $swport --  $sw->{hostname} \n";}
449
[2]450         $ret->{$sw->{hostname}} = {};
451         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
452         $ret->{$sw->{hostname}}{description} = $sw->{description};
[22]453         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
[2]454
[15]455         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
[2]456         }
457
458      $session->close;
459      }
460   return $ret;
461   }
462
463sub get_list_network {
464
[12]465   return keys %{$KLASK_CFG->{network}};
[2]466   }
467
468sub get_current_interface {
469   my $network = shift;
470
[12]471   return $KLASK_CFG->{network}{$network}{interface};
[2]472   }
[63]473
[2]474###
475# liste l'ensemble des adresses ip d'un réseau
476sub get_list_ip {
477   my @network = @_;
478
479   my $cidrlist = Net::CIDR::Lite->new;
480
481   for my $net (@network) {
[12]482      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
[2]483      for my $cmd (@line) {
[63]484         for my $method (keys %{$cmd}){
[2]485            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
486            }
487         }
488      }
489
[4]490   my @res = ();
[2]491
492   for my $cidr ($cidrlist->list()) {
493      my $net = new NetAddr::IP $cidr;
[63]494      for my $ip (@{$net}) {
495         $ip =~ s{ /32 }{}xms;
[2]496         push @res,  $ip;
497         }
498      }
499
500   return @res;
501   }
502
[9]503# liste l'ensemble des routeurs du réseau
504sub get_list_main_router {
505   my @network = @_;
506
507   my @res = ();
508
509   for my $net (@network) {
[12]510      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
[9]511      }
512
513   return @res;
514   }
515
[2]516sub get_human_readable_port {
[22]517   my $sw_model = shift;
518   my $sw_port  = shift;
[63]519
[64]520   if ($sw_model eq 'HP8000M') {
[63]521
[64]522      my $reste = (($sw_port - 1) % 8) + 1;
523      my $major = int (($sw_port - 1) / 8);
524      return "$INTERNAL_PORT_MAP{$major}$reste";
525      }
[2]526
[64]527   if ($sw_model eq 'HP2424M') {
528      if ($sw_port > 24) {
529         
530         my $reste = $sw_port - 24;
531         return "A$reste";
532         }
533      }
534
535   if ($sw_model eq 'HP1600M') {
536      if ($sw_port > 16) {
537         
538         my $reste = $sw_port - 16;
539         return "A$reste";
540         }
541      }
542
543   return $sw_port;
[2]544   }
545
546sub get_numerical_port {
[22]547   my $sw_model = shift;
548   my $sw_port  = shift;
[63]549
[64]550   if ($sw_model eq 'HP8000M') {
[2]551
[64]552      my $letter = substr $sw_port, 0, 1;
553      my $reste =  substr $sw_port, 1;
[63]554
[64]555      return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
556      }
[63]557
[64]558   if ($sw_model eq 'HP2424M') {
559      if ($sw_port =~ m/^A/xms ) {
[63]560
[64]561         my $reste =  substr $sw_port, 1;
562
563         return 24 + $reste;
564         }
565      }
566
567   if ($sw_model eq 'HP1600M') {
568      if ($sw_port =~ m/^A/xms ) {
569
570         my $reste =  substr $sw_port, 1;
571
572         return 16 + $reste;
573         }
574      }
575
576   return $sw_port;
[2]577   }
578
579################
580# Les commandes
581################
582
583sub cmd_help {
584
[63]585print <<'END';
[2]586klask - ports manager and finder for switch
587
588 klask updatedb
[69]589 klask exportdb --format [txt|html]
[2]590
[45]591 klask updatesw
[69]592 klask exportsw --format [txt|dot]
[45]593
[2]594 klask searchdb computer
595 klask search   computer
[68]596 klask search-mac-on-switch switch mac_addr
[2]597
[69]598 klask ip-free --day number_of_day [vlan_name]
599
[2]600 klask enable  switch port
601 klask disable switch port
602 klask status  switch port
603END
[63]604   return;
[2]605   }
606
[36]607sub cmd_version {
608
[63]609print <<'END';
[36]610Klask - ports manager and finder for switch
611Copyright (C) 2005-2008 Gabriel Moreau
612
613END
[37]614   print ' $Rev: 74 $'."\n";
615   print ' $Date: 2010-11-08 09:50:46 +0000 (Mon, 08 Nov 2010) $'."\n";
616   print ' $Id: klask 74 2010-11-08 09:50:46Z g7moreau $'."\n";
[63]617   return;
[36]618   }
619
[2]620sub cmd_search {
621   my @computer = @_;
[63]622
[4]623   init_switch_names();    #nomme les switchs
[2]624   fastping(@computer);
625   for my $clientname (@computer) {
626      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
627      my %where     = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement
[63]628      printf '%-22s %2i %-30s %-15s %18s', $where{switch_description}, $where{switch_port}, $resol_arp{hostname_fq}, $resol_arp{ipv4_address}, $resol_arp{mac_address}."\n"
[2]629         unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow';
630      }
[63]631   return;
[2]632   }
633
634sub cmd_searchdb {
635   my @computer = @_;
636
637   fastping(@computer);
[44]638   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[63]639
[2]640   LOOP_ON_COMPUTER:
641   for my $clientname (@computer) {
642      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
643      my $ip = $resol_arp{ipv4_address};
[63]644
[2]645      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
[63]646
647      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[2]648      $year += 1900;
649      $mon++;
[63]650      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[2]651
652      printf "%-22s %2s %-30s %-15s %-18s %s\n",
653         $computerdb->{$ip}{switch_name},
654         $computerdb->{$ip}{switch_port},
655         $computerdb->{$ip}{hostname_fq},
656         $ip,
657         $computerdb->{$ip}{mac_address},
658         $date;
659      }
[63]660   return;
[2]661   }
662
663sub cmd_updatedb {
664   my @network = @_;
665      @network = get_list_network() if not @network;
666
[34]667   test_switchdb_environnement();
668
669   my $computerdb = {};
[44]670      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
[2]671   my $timestamp = time;
[22]672
[2]673   my %computer_not_detected = ();
674   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
675
676   my $number_of_computer = get_list_ip(@network); # + 1;
[63]677   my $size_of_database   = keys %{$computerdb};
[31]678      $size_of_database   = 1 if $size_of_database == 0;
[2]679   my $i = 0;
680   my $detected_computer = 0;
[22]681
[4]682   init_switch_names('yes');    #nomme les switchs
[2]683
[22]684   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
[44]685   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[22]686   my %db_switch_output_port       = %{$switch_connection->{output_port}};
687   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
688   my %db_switch_chained_port = ();
[63]689   for my $swport (keys %db_switch_connected_on_port) {
690      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[22]691      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
692      }
693   for my $sw (@SWITCH){
694      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
695      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
696         chop $db_switch_chained_port{$sw->{hostname}};
[63]697         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
[22]698         }
699#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
700      }
701   }
702
[2]703   my %router_mac_ip = ();
704   DETECT_ALL_ROUTER:
[9]705#   for my $one_router ('194.254.66.254') {
706   for my $one_router ( get_list_main_router(@network) ) {
[2]707      my %resol_arp = resolve_ip_arp_host($one_router);
708      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
709      }
710
711   ALL_NETWORK:
712   for my $net (@network) {
713
714      my @computer = get_list_ip($net);
715      my $current_interface = get_current_interface($net);
716
717      fastping(@computer);
718
719      LOOP_ON_COMPUTER:
720      for my $one_computer (@computer) {
721         $i++;
[49]722
[63]723         my $total_percent = int (($i*100)/$number_of_computer);
724
[2]725         my $localtime = time - $timestamp;
[63]726         my ($sec,$min) = localtime $localtime;
[2]727
728         my $time_elapse = 0;
729            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
[63]730         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
[2]731
732         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
733#         printf ", Computer detected: %4i/%i (%2i%%)", $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
[63]734         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
735         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
736#         printf '  [%02i:%02i/%02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
737         printf ' %-14s', $one_computer;
[2]738
739         my %resol_arp = resolve_ip_arp_host($one_computer,$current_interface);
[63]740
[9]741         # do not search on router connection (why ?)
[2]742         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
743            $computer_not_detected{$one_computer} = $current_interface;
744            next LOOP_ON_COMPUTER;
745            }
746
[9]747         # do not search on switch inter-connection
[2]748         if (exists $switch_level{$resol_arp{hostname_fq}}) {
749            $computer_not_detected{$one_computer} = $current_interface;
750            next LOOP_ON_COMPUTER;
751            }
752
[63]753         my $switch_proposal = q{};
[2]754         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
755            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
756            }
757
[3]758         # do not have a mac address
759         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
760            $computer_not_detected{$one_computer} = $current_interface;
761            next LOOP_ON_COMPUTER;
762            }
763
[2]764         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal);
765
766         #192.168.24.156:
767         #  arp: 00:0B:DB:D5:F6:65
768         #  hostname: pcroyon.hmg.priv
769         #  port: 5
770         #  switch: sw-batH-legi:hp2524
771         #  timestamp: 1164355525
772
773         # do not have a mac address
[3]774#         if ($resol_arp{mac_address} eq 'unknow') {
775#            $computer_not_detected{$one_computer} = $current_interface;
776#            next LOOP_ON_COMPUTER;
777#            }
[2]778
779         # detected on a switch
780         if ($where{switch_description} ne 'unknow') {
781            $detected_computer++;
782            $computerdb->{$resol_arp{ipv4_address}} = {
783               hostname_fq        => $resol_arp{hostname_fq},
784               mac_address        => $resol_arp{mac_address},
785               switch_hostname    => $where{switch_hostname},
786               switch_description => $where{switch_description},
787               switch_port        => $where{switch_port},
788               timestamp          => $timestamp,
[45]789               network            => $net,
[2]790               };
791            next LOOP_ON_COMPUTER;
792            }
793
794         # new in the database but where it is ?
795         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
796            $detected_computer++;
797            $computerdb->{$resol_arp{ipv4_address}} = {
798               hostname_fq        => $resol_arp{hostname_fq},
799               mac_address        => $resol_arp{mac_address},
800               switch_hostname    => $where{switch_hostname},
801               switch_description => $where{switch_description},
802               switch_port        => $where{switch_port},
803               timestamp          => $resol_arp{timestamp},
[45]804               network            => $net,
[2]805               };
806            }
807
808         # mise a jour du nom de la machine si modification dans le dns
809         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
[63]810
[2]811         # mise à jour de la date de détection si détection plus récente par arpwatch
812         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
813
814         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
815#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
816         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
[63]817
[2]818         }
819      }
820
821   # final end of line at the end of the loop
822   printf "\n";
823
[13]824   my $dirdb = $KLASK_DB_FILE;
[63]825      $dirdb =~ s{ / [^/]* $}{}xms;
[2]826   mkdir "$dirdb", 0755 unless -d "$dirdb";
[44]827   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
[2]828
829   for my $one_computer (keys %computer_not_detected) {
830      my $interface = $computer_not_detected{$one_computer};
[16]831      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
[2]832#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
833      }
[63]834   return;
[2]835   }
836
837sub cmd_removedb {
838   my @computer = @_;
[34]839
840   test_maindb_environnement();
841
[44]842   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[2]843
844   LOOP_ON_COMPUTER:
845   for my $one_computer (@computer) {
846
[66]847      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
848            and exists $computerdb->{$one_computer} ) {
849         delete $computerdb->{$one_computer};
850         next;
851         }
852
[2]853      my %resol_arp = resolve_ip_arp_host($one_computer);
854
855      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
856      }
857
[13]858   my $dirdb = $KLASK_DB_FILE;
[63]859      $dirdb =~ s{ / [^/]* $}{}xms;
[2]860   mkdir "$dirdb", 0755 unless -d "$dirdb";
[44]861   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
[63]862   return;
[2]863   }
864
[74]865sub cmd_cleandb {
866   my @options = @_;
867
868   my $days_to_clean = 15;
869   my $verbose;
870   my $database_has_changed;
871
872   my $ret = GetOptionsFromArray(\@options,
873      'day|d=i'   => \$days_to_clean,
874      'verbose|v' => \$verbose,
875      );
876
877   my @vlan_name = get_list_network();
878
879   my $computerdb = {};
880      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
881   my $timestamp = time;
882
883   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
884
885   ALL_VLAN:
886   for my $vlan (@vlan_name) {
887
888      my @ip_list   = get_list_ip($vlan);
889      my %mactimedb = ();
890     
891      LOOP_ON_IP_ADDRESS:
892      for my $ip (@ip_list) {
893
894         next LOOP_ON_IP_ADDRESS if
895            not exists $computerdb->{$ip};
896           
897            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
898         my $ip_timestamp = $computerdb->{$ip}{timestamp};
899         my $ip_mac       = $computerdb->{$ip}{mac_address};
900         
901         $mactimedb{$ip_mac} ||= [ $ip, $ip_timestamp ];
902         
903         if ( $mactimedb{$ip_mac}->[1] - $ip_timestamp > $timestamp_barrier ) {
904            print "remove ip $ip\n" if $verbose;
905            delete $computerdb->{$ip};
906            $database_has_changed++;
907            }
908
909         if ( $ip_timestamp - $mactimedb{$ip_mac}->[1] > $timestamp_barrier ) {
910            print "remove ip ".$mactimedb{$ip_mac}->[0]."\n" if $verbose;
911            delete $computerdb->{$mactimedb{$ip_mac}->[0]};
912            $database_has_changed++;
913            }
914
915         if ( $ip_timestamp > $mactimedb{$ip_mac}->[1]) {
916            $mactimedb{$ip_mac} = [ $ip, $ip_timestamp ];
917            }
918         }
919      }
920
921   if ( $database_has_changed ) {
922      my $dirdb = $KLASK_DB_FILE;
923         $dirdb =~ s{ / [^/]* $}{}xms;
924      mkdir "$dirdb", 0755 unless -d "$dirdb";
925      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
926      }
927   return;
928   }
929
[2]930sub cmd_exportdb {
[45]931   my @ARGV   = @_;
932
933   my $format = 'txt';
934
935   my $ret = GetOptions(
936      'format|f=s'  => \$format,
937      );
938
939   my %possible_format = (
940      txt  => \&cmd_exportdb_txt,
941      html => \&cmd_exportdb_html,
942      );
943
944   $format = 'txt' if not defined $possible_format{$format};
[63]945
[45]946   $possible_format{$format}->(@ARGV);
[63]947   return;
[45]948   }
949
950sub cmd_exportdb_txt {
[34]951   test_maindb_environnement();
952
[44]953   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[2]954
[73]955   printf "%-25s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
[70]956   print "-------------------------------------------------------------------------------------------------------------------------------------------\n";
[2]957
958   LOOP_ON_IP_ADDRESS:
[63]959   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
960
[2]961#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
962
963      # to be improve in the future
964      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
965
966# dans le futur
967#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
[63]968
969      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[2]970      $year += 1900;
971      $mon++;
[63]972      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[2]973
[70]974      printf "%-26s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
[2]975         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
976         $computerdb->{$ip}{switch_port},
977         $computerdb->{$ip}{hostname_fq},
978         $ip,
979         $computerdb->{$ip}{mac_address},
[70]980         $date,
981         $computerdb->{$ip}{network} || '';
[2]982      }
[63]983   return;
[2]984   }
985
[45]986sub cmd_exportdb_html {
987   test_maindb_environnement();
988
989   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
990
991#<link rel="stylesheet" type="text/css" href="style-klask.css" />
992#<script src="sorttable-klask.js"></script>
993
[63]994   print <<'END_HTML';
[73]995<table class="sortable" summary="Klask Host Database">
[72]996 <caption>Klask Host Database</caption>
[45]997 <thead>
998  <tr>
[73]999   <th scope="col" class="klask-header-left">Switch</th>
[45]1000   <th scope="col" class="sorttable_nosort">Port</th>
1001   <th scope="col" class="sorttable_nosort">Link</th>
[73]1002   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
[45]1003   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
[67]1004   <th scope="col" class="sorttable_alpha">MAC-Address</th>
[71]1005   <th scope="col" class="sorttable_alpha">VLAN</th>
[73]1006   <th scope="col" class="klask-header-right">Date</th>
[45]1007  </tr>
1008 </thead>
1009 <tfoot>
1010  <tr>
[73]1011   <th scope="col" class="klask-footer-left">Switch</th>
[45]1012   <th scope="col" class="fklask-port">Port</th>
1013   <th scope="col" class="fklask-link">Link</th>
[73]1014   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
[45]1015   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
1016   <th scope="col" class="fklask-mac">MAC-Address</th>
[71]1017   <th scope="col" class="fklask-vlan">VLAN</th>
[73]1018   <th scope="col" class="klask-footer-right">Date</th>
[45]1019  </tr>
1020 </tfoot>
1021 <tbody>
[63]1022END_HTML
[45]1023
1024   my %mac_count = ();
1025   LOOP_ON_IP_ADDRESS:
[63]1026   foreach my $ip (keys %{$computerdb}) {
1027
[45]1028      # to be improve in the future
1029      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
[63]1030
[45]1031      $mac_count{$computerdb->{$ip}{mac_address}}++;
1032      }
1033
1034   my $typerow = 'even';
1035
1036   LOOP_ON_IP_ADDRESS:
[63]1037   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1038
[45]1039      # to be improve in the future
1040      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1041
[63]1042      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
[45]1043      $year += 1900;
1044      $mon++;
[63]1045      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[45]1046
1047#      $odd_or_even++;
1048#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
[63]1049      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1050
1051      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1052      chomp $switch_hostname;
[63]1053      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
[45]1054
[63]1055      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
[45]1056
[63]1057      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
[45]1058
[63]1059      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1060      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
[45]1061
[71]1062      my $vlan = $computerdb->{$ip}{network} || '';
1063
[63]1064      print <<"END_HTML";
[45]1065  <tr class="$typerow">
1066   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
1067   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
1068   <td><-------</td>
1069   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1070   <td sorttable_customkey="$ip_sort">$ip</td>
1071   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
[71]1072   <td>$vlan</td>
[45]1073   <td>$date</td>
1074  </tr>
[63]1075END_HTML
[45]1076      }
1077
1078   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1079
1080   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1081   my %db_switch_parent            = %{$switch_connection->{parent}};
1082   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1083   my %db_switch                   = %{$switch_connection->{switch_db}};
1084
1085   for my $sw (sort keys %db_switch_output_port) {
1086
[63]1087      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
[45]1088
[63]1089      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1090
1091      if (exists $db_switch_parent{$sw}) {
1092
1093      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1094      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1095      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1096
1097      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1098      $year += 1900;
1099      $mon++;
[63]1100      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
[45]1101
[63]1102      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
[45]1103
[63]1104      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
[45]1105
[63]1106      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
[45]1107
[63]1108      print <<"END_HTML";
[45]1109  <tr class="$typerow">
1110   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1111   <td class="bklask-port">$db_switch_output_port{$sw}</>
1112   <td>+--> $db_switch_parent{$sw}->{port}</td>
1113   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1114   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1115   <td sorttable_customkey="$mac_sort">$mac_address</td>
[71]1116   <td></td>
[45]1117   <td>$date</td>
1118  </tr>
[63]1119END_HTML
[45]1120         }
1121      else {
[63]1122         print <<"END_HTML";
[45]1123  <tr class="$typerow">
1124   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1125   <td class="bklask-port">$db_switch_output_port{$sw}</>
1126   <td>+--></td>
1127   <td sorttable_customkey="router">router</>
1128   <td sorttable_customkey="999999999999"></td>
1129   <td sorttable_customkey="99999"></td>
1130   <td></td>
[71]1131   <td></td>
[45]1132  </tr>
[63]1133END_HTML
[45]1134         }
1135      }
1136
1137   for my $swport (sort keys %db_switch_connected_on_port) {
[63]1138      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[45]1139      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1140
[63]1141         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
[45]1142
1143      my $mac_address = $db_switch{$sw}->{mac_address};
1144      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1145      my $timestamp = $db_switch{$sw}->{timestamp};
1146
1147      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1148      $year += 1900;
1149      $mon++;
[63]1150      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
[45]1151
[63]1152      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
[45]1153
[63]1154      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
[45]1155
[63]1156      $typerow = $typerow eq 'even' ? 'odd' : 'even';
[45]1157
1158         if (exists $db_switch_output_port{$sw}) {
1159
[63]1160            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
[45]1161
[63]1162            print <<"END_HTML";
[45]1163  <tr class="$typerow">
1164   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1165   <td class="bklask-port">$port_connect</>
1166   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1167   <td sorttable_customkey="$host_short">$sw</>
1168   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1169   <td sorttable_customkey="$mac_sort">$mac_address</td>
[71]1170   <td></td>
[45]1171   <td>$date</td>
1172  </tr>
[63]1173END_HTML
[45]1174            }
1175         else {
[63]1176            print <<"END_HTML";
[45]1177  <tr class="$typerow">
1178   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1179   <td class="bklask-port">$port_connect</>
1180   <td>&lt;--+</td>
1181   <td sorttable_customkey="$sw">$sw</>
1182   <td sorttable_customkey="">$ipv4_address</td>
1183   <td sorttable_customkey="">$mac_address</td>
[71]1184   <td></td>
[45]1185   <td>$date</td>
1186  </tr>
[63]1187END_HTML
[45]1188            }
1189         }
1190      }
1191
[63]1192   print <<'END_HTML';
[45]1193 </tbody>
1194</table>
[63]1195END_HTML
1196   return;
[45]1197   }
1198
[2]1199sub cmd_iplocation {
[44]1200   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
[2]1201
1202   LOOP_ON_IP_ADDRESS:
[63]1203   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
[2]1204
1205      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1206
[63]1207      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
[2]1208      next if $sw_hostname eq 'unknow';
[63]1209
1210      my $sw_location = q{};
[11]1211      for my $sw (@SWITCH) {
[2]1212         next if $sw_hostname ne $sw->{hostname};
1213         $sw_location = $sw->{location};
1214         last;
1215         }
1216
[63]1217      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
[2]1218      }
[63]1219   return;
[2]1220   }
1221
[69]1222sub cmd_ip_free {
1223   my @vlan_name = @_;
1224
1225   my $days_to_dead = 365 * 2;
1226   my $format = 'txt';
1227
1228   my $ret = GetOptionsFromArray(\@vlan_name,
1229      'day|d=i'      => \$days_to_dead,
1230      'format|f=s'   => \$format,
1231      );
1232
[72]1233   my %possible_format = (
1234      txt  => \&cmd_ip_free_txt,
1235      html => \&cmd_ip_free_html,
1236      );
1237   $format = 'txt' if not defined $possible_format{$format};
1238
[69]1239   @vlan_name = get_list_network() if not @vlan_name;
1240
1241   my $computerdb = {};
1242      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1243   my $timestamp = time;
1244
1245   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1246
[72]1247   my %result_ip = ();
[69]1248
1249   ALL_NETWORK:
1250   for my $vlan (@vlan_name) {
1251
1252      my @ip_list = get_list_ip($vlan);
1253#      my $current_interface = get_current_interface($vlan);
1254     
1255      LOOP_ON_IP_ADDRESS:
1256      for my $ip (@ip_list) {
1257
1258         next LOOP_ON_IP_ADDRESS if
1259            exists $computerdb->{$ip}
1260            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1261
1262         my $ip_date_last_detection = '';
1263         if (exists $computerdb->{$ip}) {
1264            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1265            $year += 1900;
1266            $mon++;
1267            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1268            }
1269
1270         $result_ip{$ip} ||= {};
1271         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1272
1273         my $packed_ip = scalar gethostbyname($ip);
1274         my $hostname_fq = 'unknown';
1275            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1276         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1277         
1278         $result_ip{$ip}->{vlan} = $vlan;
1279         }
1280      }
1281
[72]1282   $possible_format{$format}->(%result_ip);
1283   }
1284
1285sub cmd_ip_free_txt {
1286   my %result_ip = @_;
1287   
[69]1288   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1289   print "-------------------------------------------------------------------------------\n";
[72]1290   LOOP_ON_IP_ADDRESS:
[69]1291   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1292         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1293      }
1294   }
1295
[72]1296sub cmd_ip_free_html {
1297   my %result_ip = @_;
1298
1299   print <<'END_HTML';
[73]1300<table class="sortable" summary="Klask Free IP Database">
1301 <caption>Klask Free IP Database</caption>
[72]1302 <thead>
1303  <tr>
[73]1304   <th scope="col" class="klask-header-left">IPv4-Address</th>
[72]1305   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1306   <th scope="col" class="sorttable_alpha">VLAN</th>
[73]1307   <th scope="col" class="klask-header-right">Date</th>
[72]1308  </tr>
1309 </thead>
1310 <tfoot>
1311  <tr>
[73]1312   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1313   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
[72]1314   <th scope="col" class="fklask-vlan">VLAN</th>
[73]1315   <th scope="col" class="klask-footer-right">Date</th>
[72]1316  </tr>
1317 </tfoot>
1318 <tbody>
1319END_HTML
1320
1321   my $typerow = 'even';
1322
1323   LOOP_ON_IP_ADDRESS:
1324   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1325
1326      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1327
1328      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1329      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1330
1331      print <<"END_HTML";
1332  <tr class="$typerow">
1333   <td sorttable_customkey="$ip_sort">$ip</td>
1334   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
1335   <td>$result_ip{$ip}->{vlan}</td>
1336   <td>$result_ip{$ip}->{date_last_detection}</td>
1337  </tr>
1338END_HTML
1339      }
1340   print <<'END_HTML';
1341 </tbody>
1342</table>
1343END_HTML
1344   }
1345
[2]1346sub cmd_enable {
1347   my $switch = shift;
1348   my $port   = shift;
[63]1349
1350   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1351   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
[2]1352   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
[63]1353   return;
[2]1354   }
1355
1356sub cmd_disable {
1357   my $switch = shift;
1358   my $port   = shift;
[63]1359
[2]1360   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
[63]1361   return;
[2]1362   }
1363
1364sub cmd_status {
1365   my $switch = shift;
1366   my $port   = shift;
[63]1367
[2]1368   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
[63]1369   return;
[2]1370   }
1371
[35]1372sub cmd_search_mac_on_switch {
[63]1373   my $switch_name = shift || q{};
1374   my $mac_address = shift || q{};
1375
1376   if ($switch_name eq q{} or $mac_address eq q{}) {
[39]1377      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
[38]1378      }
[39]1379
1380   if (not defined $SWITCH_DB{$switch_name}) {
[63]1381      die "Switch $switch_name must be defined in klask configuration file\n";
[39]1382      }
1383
1384   my $sw = $SWITCH_DB{$switch_name};
1385   my %session = ( -hostname => $sw->{hostname} );
1386      $session{-version} = $sw->{version}   || 1;
1387      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
[63]1388   if (exists $sw->{version} and $sw->{version} eq '3') {
[39]1389      $session{-username} = $sw->{username} || 'snmpadmin';
1390      }
1391   else {
1392      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1393      }
1394
[63]1395   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($mac_address);
[35]1396   print "Klask search OID $research on switch $switch_name\n";
[2]1397
[39]1398   my ($session, $error) = Net::SNMP->session( %session );
[35]1399   print "$error \n" if $error;
1400
1401   my $result = $session->get_request(
1402      -varbindlist => [$research]
1403      );
[63]1404
1405   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
[35]1406      print "Klask do not find MAC $mac_address on switch $switch_name\n";
1407      $session->close;
1408      }
1409
1410   my $swport = $result->{$research};
1411   $session->close;
1412
1413   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
[63]1414   return;
[35]1415   }
1416
[4]1417sub cmd_updatesw {
[2]1418
[4]1419   init_switch_names('yes');    #nomme les switchs
[2]1420   print "\n";
1421
1422   my %where = ();
1423   my %db_switch_output_port = ();
1424   my %db_switch_ip_hostname = ();
1425
1426   DETECT_ALL_ROUTER:
[9]1427#   for my $one_computer ('194.254.66.254') {
[11]1428   for my $one_router ( get_list_main_router(get_list_network()) ) {
[63]1429      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
[2]1430      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
[13]1431      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
[2]1432      }
1433
1434   ALL_ROUTER_IP_ADDRESS:
[13]1435   for my $ip (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
[63]1436
[13]1437      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip}; # /a priori/ idiot car ne sers à rien...
[2]1438
1439      ALL_SWITCH_CONNECTED:
1440      for my $switch_detected ( keys %{$where{$ip}} ) {
1441
1442         my $switch = $where{$ip}->{$switch_detected};
1443
1444         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
[63]1445
[2]1446         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1447         }
[63]1448      }
[2]1449
1450   my %db_switch_link_with = ();
1451
1452   my @list_switch_ip = ();
1453   my @list_switch_ipv4 = ();
[11]1454   for my $sw (@SWITCH){
[2]1455      push @list_switch_ip, $sw->{hostname};
1456      }
1457
[45]1458   my $timestamp = time;
1459
[2]1460   ALL_SWITCH:
1461   for my $one_computer (@list_switch_ip) {
[63]1462      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
[2]1463      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
[63]1464
[2]1465      push @list_switch_ipv4,$resol_arp{ipv4_address};
[63]1466
[2]1467      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
[68]1468#my @l = ();
1469#for my $c  ( keys %{$where{$resol_arp{ipv4_address}}} ) {
1470#push @l, "$c -+ "; #$where{$resol_arp{ipv4_address}}->{$c}{hostname};
1471#}
1472#print "DEBUG  $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address} --- @l\n";
[2]1473
1474      $db_switch_ip_hostname{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
[45]1475
1476      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
1477      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
1478      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
[2]1479      }
[63]1480
[2]1481   ALL_SWITCH_IP_ADDRESS:
1482   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
[63]1483
[2]1484      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1485
1486      DETECTED_SWITCH:
1487      for my $switch_detected ( keys %{$where{$ip}} ) {
1488
[15]1489         next DETECTED_SWITCH if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostname{$ip}};
[2]1490
1491         my $switch = $where{$ip}->{$switch_detected};
[68]1492#print "DEBUG1 :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if  $switch->{hostname} =~ m/sw3-batA0-3s/;
[2]1493
1494         next if $switch->{port}     eq '0';
1495         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
1496         next if $switch->{hostname} eq $db_switch_ip_hostname{$ip}; # $computerdb->{$ip}{hostname};
1497
1498         $db_switch_link_with{ $db_switch_ip_hostname{$ip} } ||= {};
1499         $db_switch_link_with{ $db_switch_ip_hostname{$ip} }->{ $switch->{hostname} } = $switch->{port};
[68]1500#print "DEBUG :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if $switch->{hostname} !~ m/sw3-batA0-3s/;
[2]1501         }
1502
1503      }
[63]1504
[2]1505   my %db_switch_connected_on_port = ();
1506   my $maybe_more_than_one_switch_connected = 'yes';
[63]1507
[2]1508   while ($maybe_more_than_one_switch_connected eq 'yes') {
1509      for my $sw (keys %db_switch_link_with) {
1510         for my $connect (keys %{$db_switch_link_with{$sw}}) {
[63]1511
[2]1512            my $port = $db_switch_link_with{$sw}->{$connect};
[63]1513
[2]1514            $db_switch_connected_on_port{"$connect:$port"} ||= {};
1515            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
1516            }
1517         }
1518
1519      $maybe_more_than_one_switch_connected  = 'no';
1520
1521      SWITCH_AND_PORT:
1522      for my $swport (keys %db_switch_connected_on_port) {
[63]1523
[2]1524         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
[63]1525
[2]1526         $maybe_more_than_one_switch_connected = 'yes';
1527
[63]1528         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1529         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
1530
1531         CONNECTED:
1532         for my $sw_connected (@sw_on_same_port) {
[63]1533
[2]1534            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
[63]1535
[2]1536            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
[63]1537
[2]1538            for my $other_sw (@sw_on_same_port) {
1539               next if $other_sw eq $sw_connected;
[63]1540
[2]1541               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1542               }
[63]1543
[2]1544            # We can not do better for this switch for this loop
1545            next SWITCH_AND_PORT;
1546            }
1547         }
1548      }
1549
1550   my %db_switch_parent =();
1551
1552   for my $sw (keys %db_switch_link_with) {
1553      for my $connect (keys %{$db_switch_link_with{$sw}}) {
[63]1554
[2]1555         my $port = $db_switch_link_with{$sw}->{$connect};
[63]1556
[2]1557         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1558         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
[63]1559
[2]1560         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1561         }
1562      }
1563
[63]1564   print "Switch output port and parent port connection\n";
[2]1565   print "---------------------------------------------\n";
1566   for my $sw (sort keys %db_switch_output_port) {
1567      if (exists $db_switch_parent{$sw}) {
1568         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1569         }
1570      else {
1571         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1572         }
1573      }
1574   print "\n";
1575
1576   print "Switch parent and children port inter-connection\n";
1577   print "------------------------------------------------\n";
[63]1578   for my $swport (sort keys %db_switch_connected_on_port) {
1579      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1580      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1581         if (exists $db_switch_output_port{$sw}) {
1582            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1583            }
1584         else {
1585            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1586            }
1587         }
1588      }
1589
1590   my $switch_connection = {
1591      output_port       => \%db_switch_output_port,
1592      parent            => \%db_switch_parent,
1593      connected_on_port => \%db_switch_connected_on_port,
1594      link_with         => \%db_switch_link_with,
[25]1595      switch_db         => \%SWITCH_DB,
[2]1596      };
[63]1597
[44]1598   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
[63]1599   return;
[2]1600   }
1601
[4]1602sub cmd_exportsw {
1603   my @ARGV   = @_;
[2]1604
[34]1605   test_switchdb_environnement();
1606
[4]1607   my $format = 'txt';
1608
1609   my $ret = GetOptions(
1610      'format|f=s'  => \$format,
1611      );
1612
1613   my %possible_format = (
1614      txt => \&cmd_exportsw_txt,
1615      dot => \&cmd_exportsw_dot,
1616      );
1617
1618   $format = 'txt' if not defined $possible_format{$format};
[63]1619
[4]1620   $possible_format{$format}->(@ARGV);
[63]1621   return;
[4]1622   }
1623
1624sub cmd_exportsw_txt {
1625
[44]1626   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[4]1627
1628   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1629   my %db_switch_parent            = %{$switch_connection->{parent}};
1630   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1631
[63]1632   print "Switch output port and parent port connection\n";
[4]1633   print "---------------------------------------------\n";
1634   for my $sw (sort keys %db_switch_output_port) {
1635      if (exists $db_switch_parent{$sw}) {
1636         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1637         }
1638      else {
1639         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1640         }
1641      }
1642   print "\n";
1643
1644   print "Switch parent and children port inter-connection\n";
1645   print "------------------------------------------------\n";
[63]1646   for my $swport (sort keys %db_switch_connected_on_port) {
1647      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[4]1648      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1649         if (exists $db_switch_output_port{$sw}) {
1650            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1651            }
1652         else {
1653            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1654            }
1655         }
1656      }
[63]1657   return;
[4]1658   }
1659
1660sub cmd_exportsw_dot {
1661
[44]1662   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[63]1663
[2]1664   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1665   my %db_switch_parent            = %{$switch_connection->{parent}};
1666   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1667   my %db_switch_link_with         = %{$switch_connection->{link_with}};
[25]1668   my %db_switch_global            = %{$switch_connection->{switch_db}};
1669
[2]1670   my %db_building= ();
[11]1671   for my $sw (@SWITCH) {
[63]1672      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
[2]1673      $db_building{$building} ||= {};
1674      $db_building{$building}->{$location} ||= {};
1675      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1676      }
[63]1677
1678
[2]1679   print "digraph G {\n";
1680
[4]1681   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1682   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
[2]1683
1684   my $b=0;
1685   for my $building (keys %db_building) {
1686      $b++;
[63]1687
[4]1688      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1689      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
[2]1690
1691      my $l = 0;
1692      for my $loc (keys %{$db_building{$building}}) {
1693         $l++;
[63]1694
1695         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
[33]1696#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
[4]1697         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
[2]1698
1699         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1700
1701            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1702
[25]1703            my $swname  = $sw;
[63]1704               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
[25]1705            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
[4]1706            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1707            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
[2]1708
1709
1710            for my $swport (keys %db_switch_connected_on_port) {
[63]1711               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1712               next if not $sw_connect eq $sw;
1713               next if $port_connect eq $db_switch_output_port{$sw};
1714               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
[4]1715               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
[2]1716              }
1717            }
1718         }
1719      }
1720
[63]1721#   print "Switch output port and parent port connection\n";
[2]1722#   print "---------------------------------------------\n";
1723   for my $sw (sort keys %db_switch_output_port) {
1724      if (exists $db_switch_parent{$sw}) {
1725#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1726         }
1727      else {
1728         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1729         }
1730      }
1731   print "\n";
1732
1733#   print "Switch parent and children port inter-connection\n";
1734#   print "------------------------------------------------\n";
[63]1735   for my $swport (sort keys %db_switch_connected_on_port) {
1736      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1737      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1738         if (exists $db_switch_output_port{$sw}) {
1739            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1740            }
1741         else {
1742            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1743            }
1744         }
1745      }
1746
1747print "}\n";
[63]1748   return;
[2]1749   }
1750
1751
1752__END__
1753
1754=head1 NAME
1755
1756klask - ports manager and finder for switch
1757
1758
[63]1759=head1 USAGE
[2]1760
1761 klask updatedb
[45]1762 klask exportdb --format [txt|html]
[2]1763
[5]1764 klask updatesw
1765 klask exportsw --format [txt|dot]
1766
[2]1767 klask searchdb computer
1768 klask search   computer
[69]1769 klask search-mac-on-switch switch mac_addr
[2]1770
[69]1771 klask ip-free --day number_of_day [vlan_name]
1772
[2]1773 klask enable  switch port
1774 klask disable swith port
1775 klask status  swith port
1776
1777
1778=head1 DESCRIPTION
1779
[6]1780klask is a small tool to find where is a host in a big network. klask mean search in brittany.
[2]1781
[6]1782Klask has now a web site dedicated for it !
[2]1783
[6]1784 http://servforge.legi.inpg.fr/projects/klask
1785
1786
[2]1787=head1 COMMANDS
1788
1789
1790=head2 search
1791
1792This command takes one or more computer in argument. It search a computer on the network and give the port and the switch on which the computer is connected.
1793
1794
1795=head2 enable
1796
1797This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1798
1799
1800=head2 disable
1801
1802This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1803
1804
1805=head2 status
1806
1807This command return the status of a port number on a switch by snmp. So you need to give the switch name and the port number on the command line.
1808
1809
1810=head2 updatedb
1811
1812This command will scan networks and update a database. To know which are the cmputer scan, you have to configure the file /etc/klask.conf This file is easy to read and write because klask use YAML format and not XML.
1813
1814
1815=head2 exportdb
1816
1817This command print the content of the database. There is actually only one format. It's very easy to have more format, it's just need times...
1818
1819
[5]1820=head2 updatesw
1821
1822This command build a map of your manageable switch on your network. The list of the switch must be given in the file /etc/klask.conf.
1823
1824
1825=head2 exportsw --format [txt|dot]
1826
1827This command print the content of the switch database. There is actually two format. One is just txt for terminal and the other is the dot format from the graphviz environnement.
1828
1829 klask exportsw --format dot > /tmp/map.dot
1830 dot -Tpng /tmp/map.dot > /tmp/map.png
1831
1832
1833
[2]1834=head1 CONFIGURATION
1835
1836Because klask need many parameters, it's not possible actually to use command line parameters. The configuration is done in a /etc/klask.conf YAML file. This format have many advantage over XML, it's easier to read and to write !
1837
1838Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1839
1840 default:
1841   community: public
1842   snmpport: 161
1843
1844 network:
[5]1845   labnet:
1846     ip-subnet:
1847       - add: 192.168.1.0/24
1848       - add: 192.168.2.0/24
1849     interface: eth0
1850     main-router: gw1.labnet.local
[2]1851
[5]1852   schoolnet:
1853     ip-subnet:
1854       - add: 192.168.6.0/24
1855       - add: 192.168.7.0/24
1856     interface: eth0.38
1857     main-router: gw2.schoolnet.local
1858
[2]1859 switch:
1860   - hostname: sw1.klask.local
1861     portignore:
1862       - 1
1863       - 2
1864
1865   - hostname: sw2.klask.local
[5]1866     location: BatK / 2 / K203
1867     type: HP2424
[2]1868     portignore:
1869       - 1
1870       - 2
1871
1872I think it's pretty easy to understand. The default section can be overide in any section, if parameter mean something in theses sections. Network to be scan are define in the network section. You must put a add by network. Maybe i will make a delete line to suppress specific computers. The switch section define your switch. You have to write the port number to ignore, this is important if your switchs are cascade. Juste put the ports numbers between switch.
1873
1874
1875=head1 FILES
1876
1877 /etc/klask.conf
1878 /var/cache/klask/klaskdb
[7]1879 /var/cache/klask/switchdb
[2]1880
1881=head1 SEE ALSO
1882
1883Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1884
1885
1886=head1 VERSION
1887
[36]1888$Id: klask 74 2010-11-08 09:50:46Z g7moreau $
[2]1889
1890
1891=head1 AUTHOR
1892
[5]1893Written by Gabriel Moreau, Grenoble - France
[2]1894
1895
[63]1896=head1 LICENSE AND COPYRIGHT
[2]1897
1898GPL version 2 or later and Perl equivalent
[45]1899
[63]1900Copyright (C) 2005-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.