source: trunk/klask @ 82

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