source: trunk/klask @ 96

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