source: trunk/klask @ 73

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