source: trunk/klask @ 71

Last change on this file since 71 was 71, checked in by g7moreau, 14 years ago
  • Add VLAN columns to exportdb for html format
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 58.1 KB
RevLine 
[2]1#!/usr/bin/perl -w
[32]2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 71 2010-11-02 16:31:51Z 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: 71 $'."\n";
614   print ' $Date: 2010-11-02 16:31:51 +0000 (Tue, 02 Nov 2010) $'."\n";
615   print ' $Id: klask 71 2010-11-02 16:31:51Z 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
[70]889   printf "%-25s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname IPv4-Address MAC-Address Date VLAN);
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';
[45]929<table class="sortable" summary="Klask export database">
930 <caption>Klask database</caption>
931 <thead>
932  <tr>
933   <th scope="col" class="hklask-switch">Switch</th>
934   <th scope="col" class="sorttable_nosort">Port</th>
935   <th scope="col" class="sorttable_nosort">Link</th>
936   <th scope="col" class="sorttable_alpha">Hostname</th>
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>
[45]940   <th scope="col" class="hklask-date">Date</th>
941  </tr>
942 </thead>
943 <tfoot>
944  <tr>
945   <th scope="col" class="fklask-switch">Switch</th>
946   <th scope="col" class="fklask-port">Port</th>
947   <th scope="col" class="fklask-link">Link</th>
948   <th scope="col" class="fklask-hostname">Hostname</th>
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>
[45]952   <th scope="col" class="fklask-date">Date</th>
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
1167   @vlan_name = get_list_network() if not @vlan_name;
1168
1169#   my %possible_format = (
1170#      txt  => \&cmd_exportdb_txt,
1171#      html => \&cmd_exportdb_html,
1172#      );
1173#
1174#   $format = 'txt' if not defined $possible_format{$format};
1175#
1176#   $possible_format{$format}->(@ARGV);
1177
1178   my $computerdb = {};
1179      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1180   my $timestamp = time;
1181
1182   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1183
1184   my %result_ip= ();
1185
1186   ALL_NETWORK:
1187   for my $vlan (@vlan_name) {
1188
1189      my @ip_list = get_list_ip($vlan);
1190#      my $current_interface = get_current_interface($vlan);
1191     
1192      LOOP_ON_IP_ADDRESS:
1193      for my $ip (@ip_list) {
1194
1195         next LOOP_ON_IP_ADDRESS if
1196            exists $computerdb->{$ip}
1197            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1198
1199         my $ip_date_last_detection = '';
1200         if (exists $computerdb->{$ip}) {
1201            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1202            $year += 1900;
1203            $mon++;
1204            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1205            }
1206
1207         $result_ip{$ip} ||= {};
1208         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1209
1210         my $packed_ip = scalar gethostbyname($ip);
1211         my $hostname_fq = 'unknown';
1212            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1213         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1214         
1215         $result_ip{$ip}->{vlan} = $vlan;
1216         }
1217      }
1218
1219   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1220   print "-------------------------------------------------------------------------------\n";
1221   LOOP_ON_RESULT:
1222   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1223         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1224      }
1225   }
1226
[2]1227sub cmd_enable {
1228   my $switch = shift;
1229   my $port   = shift;
[63]1230
1231   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1232   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
[2]1233   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
[63]1234   return;
[2]1235   }
1236
1237sub cmd_disable {
1238   my $switch = shift;
1239   my $port   = shift;
[63]1240
[2]1241   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
[63]1242   return;
[2]1243   }
1244
1245sub cmd_status {
1246   my $switch = shift;
1247   my $port   = shift;
[63]1248
[2]1249   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
[63]1250   return;
[2]1251   }
1252
[35]1253sub cmd_search_mac_on_switch {
[63]1254   my $switch_name = shift || q{};
1255   my $mac_address = shift || q{};
1256
1257   if ($switch_name eq q{} or $mac_address eq q{}) {
[39]1258      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
[38]1259      }
[39]1260
1261   if (not defined $SWITCH_DB{$switch_name}) {
[63]1262      die "Switch $switch_name must be defined in klask configuration file\n";
[39]1263      }
1264
1265   my $sw = $SWITCH_DB{$switch_name};
1266   my %session = ( -hostname => $sw->{hostname} );
1267      $session{-version} = $sw->{version}   || 1;
1268      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
[63]1269   if (exists $sw->{version} and $sw->{version} eq '3') {
[39]1270      $session{-username} = $sw->{username} || 'snmpadmin';
1271      }
1272   else {
1273      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1274      }
1275
[63]1276   my $research = '1.3.6.1.2.1.17.4.3.1.2' . arp_hex_to_dec($mac_address);
[35]1277   print "Klask search OID $research on switch $switch_name\n";
[2]1278
[39]1279   my ($session, $error) = Net::SNMP->session( %session );
[35]1280   print "$error \n" if $error;
1281
1282   my $result = $session->get_request(
1283      -varbindlist => [$research]
1284      );
[63]1285
1286   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
[35]1287      print "Klask do not find MAC $mac_address on switch $switch_name\n";
1288      $session->close;
1289      }
1290
1291   my $swport = $result->{$research};
1292   $session->close;
1293
1294   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
[63]1295   return;
[35]1296   }
1297
[4]1298sub cmd_updatesw {
[2]1299
[4]1300   init_switch_names('yes');    #nomme les switchs
[2]1301   print "\n";
1302
1303   my %where = ();
1304   my %db_switch_output_port = ();
1305   my %db_switch_ip_hostname = ();
1306
1307   DETECT_ALL_ROUTER:
[9]1308#   for my $one_computer ('194.254.66.254') {
[11]1309   for my $one_router ( get_list_main_router(get_list_network()) ) {
[63]1310      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
[2]1311      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
[13]1312      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
[2]1313      }
1314
1315   ALL_ROUTER_IP_ADDRESS:
[13]1316   for my $ip (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
[63]1317
[13]1318      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip}; # /a priori/ idiot car ne sers à rien...
[2]1319
1320      ALL_SWITCH_CONNECTED:
1321      for my $switch_detected ( keys %{$where{$ip}} ) {
1322
1323         my $switch = $where{$ip}->{$switch_detected};
1324
1325         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
[63]1326
[2]1327         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1328         }
[63]1329      }
[2]1330
1331   my %db_switch_link_with = ();
1332
1333   my @list_switch_ip = ();
1334   my @list_switch_ipv4 = ();
[11]1335   for my $sw (@SWITCH){
[2]1336      push @list_switch_ip, $sw->{hostname};
1337      }
1338
[45]1339   my $timestamp = time;
1340
[2]1341   ALL_SWITCH:
1342   for my $one_computer (@list_switch_ip) {
[63]1343      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
[2]1344      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
[63]1345
[2]1346      push @list_switch_ipv4,$resol_arp{ipv4_address};
[63]1347
[2]1348      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
[68]1349#my @l = ();
1350#for my $c  ( keys %{$where{$resol_arp{ipv4_address}}} ) {
1351#push @l, "$c -+ "; #$where{$resol_arp{ipv4_address}}->{$c}{hostname};
1352#}
1353#print "DEBUG  $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address} --- @l\n";
[2]1354
1355      $db_switch_ip_hostname{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
[45]1356
1357      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
1358      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
1359      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
[2]1360      }
[63]1361
[2]1362   ALL_SWITCH_IP_ADDRESS:
1363   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
[63]1364
[2]1365      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1366
1367      DETECTED_SWITCH:
1368      for my $switch_detected ( keys %{$where{$ip}} ) {
1369
[15]1370         next DETECTED_SWITCH if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostname{$ip}};
[2]1371
1372         my $switch = $where{$ip}->{$switch_detected};
[68]1373#print "DEBUG1 :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if  $switch->{hostname} =~ m/sw3-batA0-3s/;
[2]1374
1375         next if $switch->{port}     eq '0';
1376         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
1377         next if $switch->{hostname} eq $db_switch_ip_hostname{$ip}; # $computerdb->{$ip}{hostname};
1378
1379         $db_switch_link_with{ $db_switch_ip_hostname{$ip} } ||= {};
1380         $db_switch_link_with{ $db_switch_ip_hostname{$ip} }->{ $switch->{hostname} } = $switch->{port};
[68]1381#print "DEBUG :  $db_switch_ip_hostname{$ip} / $switch->{hostname} : $switch->{port}\n" if $switch->{hostname} !~ m/sw3-batA0-3s/;
[2]1382         }
1383
1384      }
[63]1385
[2]1386   my %db_switch_connected_on_port = ();
1387   my $maybe_more_than_one_switch_connected = 'yes';
[63]1388
[2]1389   while ($maybe_more_than_one_switch_connected eq 'yes') {
1390      for my $sw (keys %db_switch_link_with) {
1391         for my $connect (keys %{$db_switch_link_with{$sw}}) {
[63]1392
[2]1393            my $port = $db_switch_link_with{$sw}->{$connect};
[63]1394
[2]1395            $db_switch_connected_on_port{"$connect:$port"} ||= {};
1396            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
1397            }
1398         }
1399
1400      $maybe_more_than_one_switch_connected  = 'no';
1401
1402      SWITCH_AND_PORT:
1403      for my $swport (keys %db_switch_connected_on_port) {
[63]1404
[2]1405         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
[63]1406
[2]1407         $maybe_more_than_one_switch_connected = 'yes';
1408
[63]1409         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1410         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
1411
1412         CONNECTED:
1413         for my $sw_connected (@sw_on_same_port) {
[63]1414
[2]1415            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
[63]1416
[2]1417            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
[63]1418
[2]1419            for my $other_sw (@sw_on_same_port) {
1420               next if $other_sw eq $sw_connected;
[63]1421
[2]1422               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1423               }
[63]1424
[2]1425            # We can not do better for this switch for this loop
1426            next SWITCH_AND_PORT;
1427            }
1428         }
1429      }
1430
1431   my %db_switch_parent =();
1432
1433   for my $sw (keys %db_switch_link_with) {
1434      for my $connect (keys %{$db_switch_link_with{$sw}}) {
[63]1435
[2]1436         my $port = $db_switch_link_with{$sw}->{$connect};
[63]1437
[2]1438         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1439         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
[63]1440
[2]1441         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1442         }
1443      }
1444
[63]1445   print "Switch output port and parent port connection\n";
[2]1446   print "---------------------------------------------\n";
1447   for my $sw (sort keys %db_switch_output_port) {
1448      if (exists $db_switch_parent{$sw}) {
1449         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1450         }
1451      else {
1452         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1453         }
1454      }
1455   print "\n";
1456
1457   print "Switch parent and children port inter-connection\n";
1458   print "------------------------------------------------\n";
[63]1459   for my $swport (sort keys %db_switch_connected_on_port) {
1460      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1461      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1462         if (exists $db_switch_output_port{$sw}) {
1463            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1464            }
1465         else {
1466            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1467            }
1468         }
1469      }
1470
1471   my $switch_connection = {
1472      output_port       => \%db_switch_output_port,
1473      parent            => \%db_switch_parent,
1474      connected_on_port => \%db_switch_connected_on_port,
1475      link_with         => \%db_switch_link_with,
[25]1476      switch_db         => \%SWITCH_DB,
[2]1477      };
[63]1478
[44]1479   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
[63]1480   return;
[2]1481   }
1482
[4]1483sub cmd_exportsw {
1484   my @ARGV   = @_;
[2]1485
[34]1486   test_switchdb_environnement();
1487
[4]1488   my $format = 'txt';
1489
1490   my $ret = GetOptions(
1491      'format|f=s'  => \$format,
1492      );
1493
1494   my %possible_format = (
1495      txt => \&cmd_exportsw_txt,
1496      dot => \&cmd_exportsw_dot,
1497      );
1498
1499   $format = 'txt' if not defined $possible_format{$format};
[63]1500
[4]1501   $possible_format{$format}->(@ARGV);
[63]1502   return;
[4]1503   }
1504
1505sub cmd_exportsw_txt {
1506
[44]1507   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[4]1508
1509   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1510   my %db_switch_parent            = %{$switch_connection->{parent}};
1511   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1512
[63]1513   print "Switch output port and parent port connection\n";
[4]1514   print "---------------------------------------------\n";
1515   for my $sw (sort keys %db_switch_output_port) {
1516      if (exists $db_switch_parent{$sw}) {
1517         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1518         }
1519      else {
1520         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1521         }
1522      }
1523   print "\n";
1524
1525   print "Switch parent and children port inter-connection\n";
1526   print "------------------------------------------------\n";
[63]1527   for my $swport (sort keys %db_switch_connected_on_port) {
1528      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[4]1529      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1530         if (exists $db_switch_output_port{$sw}) {
1531            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1532            }
1533         else {
1534            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1535            }
1536         }
1537      }
[63]1538   return;
[4]1539   }
1540
1541sub cmd_exportsw_dot {
1542
[44]1543   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
[63]1544
[2]1545   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1546   my %db_switch_parent            = %{$switch_connection->{parent}};
1547   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1548   my %db_switch_link_with         = %{$switch_connection->{link_with}};
[25]1549   my %db_switch_global            = %{$switch_connection->{switch_db}};
1550
[2]1551   my %db_building= ();
[11]1552   for my $sw (@SWITCH) {
[63]1553      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
[2]1554      $db_building{$building} ||= {};
1555      $db_building{$building}->{$location} ||= {};
1556      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1557      }
[63]1558
1559
[2]1560   print "digraph G {\n";
1561
[4]1562   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1563   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
[2]1564
1565   my $b=0;
1566   for my $building (keys %db_building) {
1567      $b++;
[63]1568
[4]1569      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1570      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
[2]1571
1572      my $l = 0;
1573      for my $loc (keys %{$db_building{$building}}) {
1574         $l++;
[63]1575
1576         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
[33]1577#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
[4]1578         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
[2]1579
1580         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1581
1582            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1583
[25]1584            my $swname  = $sw;
[63]1585               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
[25]1586            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
[4]1587            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1588            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
[2]1589
1590
1591            for my $swport (keys %db_switch_connected_on_port) {
[63]1592               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1593               next if not $sw_connect eq $sw;
1594               next if $port_connect eq $db_switch_output_port{$sw};
1595               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
[4]1596               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
[2]1597              }
1598            }
1599         }
1600      }
1601
[63]1602#   print "Switch output port and parent port connection\n";
[2]1603#   print "---------------------------------------------\n";
1604   for my $sw (sort keys %db_switch_output_port) {
1605      if (exists $db_switch_parent{$sw}) {
1606#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1607         }
1608      else {
1609         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1610         }
1611      }
1612   print "\n";
1613
1614#   print "Switch parent and children port inter-connection\n";
1615#   print "------------------------------------------------\n";
[63]1616   for my $swport (sort keys %db_switch_connected_on_port) {
1617      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
[2]1618      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1619         if (exists $db_switch_output_port{$sw}) {
1620            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1621            }
1622         else {
1623            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1624            }
1625         }
1626      }
1627
1628print "}\n";
[63]1629   return;
[2]1630   }
1631
1632
1633__END__
1634
1635=head1 NAME
1636
1637klask - ports manager and finder for switch
1638
1639
[63]1640=head1 USAGE
[2]1641
1642 klask updatedb
[45]1643 klask exportdb --format [txt|html]
[2]1644
[5]1645 klask updatesw
1646 klask exportsw --format [txt|dot]
1647
[2]1648 klask searchdb computer
1649 klask search   computer
[69]1650 klask search-mac-on-switch switch mac_addr
[2]1651
[69]1652 klask ip-free --day number_of_day [vlan_name]
1653
[2]1654 klask enable  switch port
1655 klask disable swith port
1656 klask status  swith port
1657
1658
1659=head1 DESCRIPTION
1660
[6]1661klask is a small tool to find where is a host in a big network. klask mean search in brittany.
[2]1662
[6]1663Klask has now a web site dedicated for it !
[2]1664
[6]1665 http://servforge.legi.inpg.fr/projects/klask
1666
1667
[2]1668=head1 COMMANDS
1669
1670
1671=head2 search
1672
1673This 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.
1674
1675
1676=head2 enable
1677
1678This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1679
1680
1681=head2 disable
1682
1683This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1684
1685
1686=head2 status
1687
1688This 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.
1689
1690
1691=head2 updatedb
1692
1693This 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.
1694
1695
1696=head2 exportdb
1697
1698This 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...
1699
1700
[5]1701=head2 updatesw
1702
1703This 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.
1704
1705
1706=head2 exportsw --format [txt|dot]
1707
1708This 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.
1709
1710 klask exportsw --format dot > /tmp/map.dot
1711 dot -Tpng /tmp/map.dot > /tmp/map.png
1712
1713
1714
[2]1715=head1 CONFIGURATION
1716
1717Because 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 !
1718
1719Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1720
1721 default:
1722   community: public
1723   snmpport: 161
1724
1725 network:
[5]1726   labnet:
1727     ip-subnet:
1728       - add: 192.168.1.0/24
1729       - add: 192.168.2.0/24
1730     interface: eth0
1731     main-router: gw1.labnet.local
[2]1732
[5]1733   schoolnet:
1734     ip-subnet:
1735       - add: 192.168.6.0/24
1736       - add: 192.168.7.0/24
1737     interface: eth0.38
1738     main-router: gw2.schoolnet.local
1739
[2]1740 switch:
1741   - hostname: sw1.klask.local
1742     portignore:
1743       - 1
1744       - 2
1745
1746   - hostname: sw2.klask.local
[5]1747     location: BatK / 2 / K203
1748     type: HP2424
[2]1749     portignore:
1750       - 1
1751       - 2
1752
1753I 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.
1754
1755
1756=head1 FILES
1757
1758 /etc/klask.conf
1759 /var/cache/klask/klaskdb
[7]1760 /var/cache/klask/switchdb
[2]1761
1762=head1 SEE ALSO
1763
1764Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1765
1766
1767=head1 VERSION
1768
[36]1769$Id: klask 71 2010-11-02 16:31:51Z g7moreau $
[2]1770
1771
1772=head1 AUTHOR
1773
[5]1774Written by Gabriel Moreau, Grenoble - France
[2]1775
1776
[63]1777=head1 LICENSE AND COPYRIGHT
[2]1778
1779GPL version 2 or later and Perl equivalent
[45]1780
[63]1781Copyright (C) 2005-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.