source: trunk/klask @ 83

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