source: trunk/klask @ 112

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