source: trunk/klask @ 109

Last change on this file since 109 was 109, checked in by g7moreau, 12 years ago
  • Shuffle list before clean it. We just need to test between the most recent date, with shuffle, te newrest date will change everytime...
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 61.3 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 109 2012-03-12 21:34:44Z 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 GetOptionsFromArray);
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
32#my $DEBUG = 0;
33#GetOptionsFromArray(\@ARGV,
34#   'debug|d=i'   => \$DEBUG,
35#   );
36
37test_running_environnement();
38
39my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
40
41my %DEFAULT = %{ $KLASK_CFG->{default} };
42my @SWITCH  = @{ $KLASK_CFG->{switch}  };
43
44my %switch_level = ();
45my %SWITCH_DB    = ();
46LEVEL_OF_EACH_SWITCH:
47for my $sw (@SWITCH){
48   $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
49   $SWITCH_DB{$sw->{hostname}} = $sw;
50   }
51@SWITCH = reverse sort { $switch_level{$a->{hostname}} <=> $switch_level{$b->{hostname}} } @{$KLASK_CFG->{switch}};
52
53my %SWITCH_PORT_COUNT = ();
54
55my %CMD_DB = (
56   help       => \&cmd_help,
57   version    => \&cmd_version,
58   exportdb   => \&cmd_exportdb,
59   updatedb   => \&cmd_updatedb,
60   searchdb   => \&cmd_searchdb,
61   removedb   => \&cmd_removedb,
62   cleandb   => \&cmd_cleandb,
63   search     => \&cmd_search,
64   enable     => \&cmd_enable,
65   disable    => \&cmd_disable,
66   status     => \&cmd_status,
67   updatesw   => \&cmd_updatesw,
68   exportsw   => \&cmd_exportsw,
69   iplocation => \&cmd_iplocation,
70   'ip-free'  => \&cmd_ip_free,
71   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
72   );
73
74Readonly my %INTERNAL_PORT_MAP => (
75   0 => 'A',
76   1 => 'B',
77   2 => 'C',
78   3 => 'D',
79   4 => 'E',
80   5 => 'F',
81   6 => 'G',
82   7 => 'H',
83   );
84Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP;
85
86Readonly my %SWITCH_KIND => (
87   J3299A => { model => 'HP224M',     match => 'HP J3299A ProCurve Switch 224M'  },
88   J4120A => { model => 'HP1600M',    match => 'HP J4120A ProCurve Switch 1600M' },
89   J9029A => { model => 'HP1800-8G',  match => 'PROCURVE J9029A'                 },
90   J9449A => { model => 'HP1810-8G',  match => 'HP ProCurve 1810G - 8 GE'        },
91   J4093A => { model => 'HP2424M',    match => 'HP J4093A ProCurve Switch 2424M' },
92   J9279A => { model => 'HP2510G-24', match => 'ProCurve J9279A Switch 2510G-24' },
93   J9280A => { model => 'HP2510G-48', match => 'ProCurve J9280A Switch 2510G-48' },
94   J4813A => { model => 'HP2524',     match => 'HP J4813A ProCurve Switch 2524'  },
95   J4900A => { model => 'HP2626A',    match => 'HP J4900A ProCurve Switch 2626'  },
96   J4900B => { model => 'HP2626B',    match => 'J4900B.+?Switch 2626'            },# ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626
97   J4899B => { model => 'HP2650',     match => 'ProCurve J4899B Switch 2650'     },
98   J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G' },
99   J9022A => { model => 'HP2810-48G', match => 'ProCurve J9022A Switch 2810-48G' },
100   J4903A => { model => 'HP2824',     match => 'J4903A.+?Switch 2824,'           },
101   J4110A => { model => 'HP8000M',    match => 'HP J4110A ProCurve Switch 8000M' },
102   BS350T => { model => 'BS350T',     match => 'BayStack 350T HW'                },
103   );
104
105Readonly my %OID_NUMBER => (
106   sysDescription  => '1.3.6.1.2.1.1.1.0',
107   sysName         => '1.3.6.1.2.1.1.5.0',
108   sysContact      => '1.3.6.1.2.1.1.4.0',
109   sysLocation     => '1.3.6.1.2.1.1.6.0',
110   searchPort      => '1.3.6.1.2.1.17.4.3.1.2',
111   );
112
113Readonly 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;
114Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
115
116
117################
118# principal
119################
120
121my $cmd = shift @ARGV || 'help';
122if (defined $CMD_DB{$cmd}) {
123   $CMD_DB{$cmd}->(@ARGV);
124   }
125else {
126   print {*STDERR} "klask: command $cmd not found\n\n";
127   $CMD_DB{help}->();
128   exit 1;
129   }
130
131exit;
132
133sub test_running_environnement {
134   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
135   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
136   return;
137   }
138
139sub test_switchdb_environnement {
140   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
141   return;
142   }
143
144sub test_maindb_environnement {
145   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
146   return;
147   }
148
149###
150# fast ping dont l'objectif est de remplir la table arp de la machine
151sub fastping {
152   # Launch this command without waiting...
153   system "fping -c 1 @_ >/dev/null 2>&1 &";
154   return;
155   }
156
157sub shell_command {
158   my $cmd = shift;
159
160   my $fh     = new FileHandle;
161   my $result = '';
162   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
163   $result .= <$fh>;
164   close $fh;
165   chomp $result;
166   return $result;
167   }
168
169###
170# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
171sub resolve_ip_arp_host {
172   my $param_ip_or_host = shift;
173   my $interface = shift || q{*};
174   my $type      = shift || q{fast};
175
176   my %ret = (
177      hostname_fq  => 'unknow',
178      ipv4_address => '0.0.0.0',
179      mac_address  => 'unknow',
180      );
181
182   # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))'
183   my $packed_ip = scalar gethostbyname($param_ip_or_host);
184   return %ret if not defined $packed_ip;
185   $ret{ipv4_address} = inet_ntoa($packed_ip);
186
187   # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)'
188   my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET);
189   $ret{hostname_fq} = $hostname_fq if defined $hostname_fq;
190
191   # my $cmd = q{grep  -he '\b} . $param_ip_or_host . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
192   my $cmd = q{grep  -he '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
193   my $cmd_arpwatch = shell_command $cmd;
194   my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
195
196   $ret{mac_address}  = $arp       if $arp;
197   $ret{timestamp}    = $timestamp if $timestamp;
198
199   my $nowtimestamp = time;
200
201   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 45 * 60 ) ) ) { # 45 min
202      $ret{mac_address} = 'unknow';
203      return %ret;
204      }
205
206   # resultat de la commande arp
207   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
208   # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37
209   my $cmd_arp  = shell_command "arp -a $param_ip_or_host";
210   if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) {
211      ( $ret{hostname_fq}, $ret{ipv4_address}, $ret{mac_address} )  = ($1, $2, $3);
212      }
213
214   # Normalize MAC Address
215   if ($ret{mac_address} ne 'unknow') {
216      my @paquets = ();
217      foreach ( split m/ : /xms, $ret{mac_address} ) {
218         my @chars = split m//xms, uc "00$_";
219         push @paquets, "$chars[-2]$chars[-1]";
220         }
221      $ret{mac_address} = join q{:}, @paquets;
222      }
223
224   return %ret;
225   }
226
227# Find Surname of a switch
228sub get_switch_model {
229   my $sw_snmp_description = shift || 'unknow';
230
231   for my $sw_kind (keys %SWITCH_KIND) {
232      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ?
233
234      return $SWITCH_KIND{$sw_kind}->{model};
235      }
236
237   return $sw_snmp_description;
238   }
239
240###
241# va rechercher le nom des switchs pour savoir qui est qui
242sub init_switch_names {
243   my $verbose = shift;
244
245   printf "%-26s                %-25s %s\n",'Switch','Description','Type';
246   print "------------------------------------------------------------------------------\n" if $verbose;
247
248   INIT_EACH_SWITCH:
249   for my $sw (@SWITCH) {
250      my %session = ( -hostname   => $sw->{hostname} );
251         $session{-version} = $sw->{version}   || 1;
252         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
253         if (exists $sw->{version} and $sw->{version} eq '3') {
254            $session{-username} = $sw->{username} || 'snmpadmin';
255            }
256         else {
257            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
258            }
259
260      $sw->{local_session} = \%session;
261
262      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
263      print "$error \n" if $error;
264
265      my $result = $session->get_request(
266         -varbindlist => [
267            $OID_NUMBER{sysDescription},
268            $OID_NUMBER{sysName},
269            $OID_NUMBER{sysContact},
270            $OID_NUMBER{sysLocation},
271            ]
272         );
273      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
274      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescription}});
275      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
276      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
277      $session->close;
278
279      # Ligne à virer car on récupère maintenant le modèle du switch
280      my ($desc, $type) = split m/ : /xms, $sw->{description}, 2;
281      printf "%-26s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
282      }
283
284   print "\n" if $verbose;
285   return;
286   }
287
288###
289# convertit l'hexa (uniquement 2 chiffres) en decimal
290sub digit_hex_to_dec {
291   #00:0F:1F:43:E4:2B
292   my $car = '00' . uc shift;
293
294   return '00' if $car eq '00UNKNOW';
295   my %table = (
296      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
297      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
298      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
299      );
300   my @chars = split m//xms, $car;
301   return $table{$chars[-2]}*16 + $table{$chars[-1]};
302   }
303
304###
305# convertit l'@ mac en decimal
306sub mac_address_hex_to_dec {
307   #00:0F:1F:43:E4:2B
308   my $mac_address = shift;
309
310   my @paquets = split m/ : /xms, $mac_address;
311   my $return = q{};
312   foreach(@paquets) {
313      $return .= q{.} . digit_hex_to_dec($_);
314      }
315   return $return;
316   }
317
318###
319# va rechercher le port et le switch sur lequel est la machine
320sub find_switch_port {
321   my $mac_address     = shift;
322   my $switch_proposal = shift || q{};
323
324   my %ret;
325   $ret{switch_description} = 'unknow';
326   $ret{switch_port} = '0';
327
328   return %ret if $mac_address eq 'unknow';;
329
330   my @switch_search = @SWITCH;
331   if ($switch_proposal ne q{}) {
332      for my $sw (@SWITCH) {
333         next if $sw->{hostname} ne $switch_proposal;
334         unshift @switch_search, $sw;
335         last;
336         }
337      }
338
339   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
340
341   LOOP_ON_SWITCH:
342   for my $sw (@switch_search) {
343      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
344      print "$error \n" if $error;
345
346      my $result = $session->get_request(
347         -varbindlist => [$research]
348         );
349      if (not defined $result or $result->{$research} eq 'noSuchInstance') {
350         $session->close;
351         next LOOP_ON_SWITCH;
352         }
353
354         my $swport = $result->{$research};
355         $session->close;
356
357         # IMPORTANT !!
358         # ceci empeche la detection sur certains port ...
359         # en effet les switch sont relies entre eux par un cable reseau et du coup
360         # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
361         # cette partie est a ameliore, voir a configurer dans l'entete
362         # 21->24 45->48
363#         my $flag = 0;
364         SWITCH_PORT_IGNORE:
365         foreach my $p (@{$sw->{portignore}}) {
366            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
367#            $flag = 1;
368            next LOOP_ON_SWITCH;
369            }
370#         if ($flag == 0) {
371            $ret{switch_hostname}    = $sw->{hostname};
372            $ret{switch_description} = $sw->{description};
373            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
374
375            last LOOP_ON_SWITCH;
376#            }
377#         }
378#      $session->close;
379      }
380   return %ret;
381   }
382
383###
384# va rechercher les port et les switch sur lequel est la machine
385sub find_all_switch_port {
386   my $mac_address = shift;
387
388   my $ret = {};
389
390   return $ret if $mac_address eq 'unknow';
391
392#   for my $sw (@SWITCH) {
393#      next if exists $SWITCH_PORT_COUNT{$sw->{hostname}};
394#
395#      $SWITCH_PORT_COUNT{$sw->{hostname}} = {};
396#      print "DEBUG: SWITCH_PORT_COUNT defined for $sw->{hostname}\n" if $DEBUG xor 2;
397#      }
398
399   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
400   LOOP_ON_ALL_SWITCH:
401   for my $sw (@SWITCH) {
402      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
403      print "$error \n" if $error;
404
405      my $result = $session->get_request(
406         -varbindlist => [$research]
407         );
408
409      if(defined $result and $result->{$research} ne 'noSuchInstance'){
410         my $swport = $result->{$research};
411
412         $ret->{$sw->{hostname}} = {};
413         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
414         $ret->{$sw->{hostname}}{description} = $sw->{description};
415         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
416
417#         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
418         }
419
420      $session->close;
421      }
422   return $ret;
423   }
424
425sub get_list_network {
426
427   return keys %{$KLASK_CFG->{network}};
428   }
429
430sub get_current_interface {
431   my $network = shift;
432
433   return $KLASK_CFG->{network}{$network}{interface};
434   }
435
436###
437# liste l'ensemble des adresses ip d'un réseau
438sub get_list_ip {
439   my @network = @_;
440
441   my $cidrlist = Net::CIDR::Lite->new;
442
443   for my $net (@network) {
444      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
445      for my $cmd (@line) {
446         for my $method (keys %{$cmd}){
447            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
448            }
449         }
450      }
451
452   my @res = ();
453
454   for my $cidr ($cidrlist->list()) {
455      my $net = new NetAddr::IP $cidr;
456      for my $ip (@{$net}) {
457         $ip =~ s{ /32 }{}xms;
458         push @res,  $ip;
459         }
460      }
461
462   return @res;
463   }
464
465# liste l'ensemble des routeurs du réseau
466sub get_list_main_router {
467   my @network = @_;
468
469   my @res = ();
470
471   for my $net (@network) {
472      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
473      }
474
475   return @res;
476   }
477
478sub get_human_readable_port {
479   my $sw_model = shift;
480   my $sw_port  = shift;
481
482   if ($sw_model eq 'HP8000M') {
483
484      my $reste = (($sw_port - 1) % 8) + 1;
485      my $major = int (($sw_port - 1) / 8);
486      return "$INTERNAL_PORT_MAP{$major}$reste";
487      }
488
489   if ($sw_model eq 'HP2424M') {
490      if ($sw_port > 24) {
491         
492         my $reste = $sw_port - 24;
493         return "A$reste";
494         }
495      }
496
497   if ($sw_model eq 'HP1600M') {
498      if ($sw_port > 16) {
499         
500         my $reste = $sw_port - 16;
501         return "A$reste";
502         }
503      }
504
505   return $sw_port;
506   }
507
508sub get_numerical_port {
509   my $sw_model = shift;
510   my $sw_port  = shift;
511
512   if ($sw_model eq 'HP8000M') {
513
514      my $letter = substr $sw_port, 0, 1;
515      my $reste =  substr $sw_port, 1;
516
517      return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
518      }
519
520   if ($sw_model eq 'HP2424M') {
521      if ($sw_port =~ m/^A/xms ) {
522
523         my $reste =  substr $sw_port, 1;
524
525         return 24 + $reste;
526         }
527      }
528
529   if ($sw_model eq 'HP1600M') {
530      if ($sw_port =~ m/^A/xms ) {
531
532         my $reste =  substr $sw_port, 1;
533
534         return 16 + $reste;
535         }
536      }
537
538   return $sw_port;
539   }
540
541################
542# Les commandes
543################
544
545sub cmd_help {
546
547print <<'END';
548klask - ports manager and finder for switch
549
550 klask updatedb
551 klask exportdb --format [txt|html]
552 klask removedb computer*
553 klask cleandb  --day number_of_day --verbose
554
555 klask updatesw
556 klask exportsw --format [txt|dot]
557
558 klask searchdb computer
559 klask search   computer
560 klask search-mac-on-switch switch mac_addr
561
562 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
563
564 klask enable  switch port
565 klask disable switch port
566 klask status  switch port
567END
568   return;
569   }
570
571sub cmd_version {
572
573print <<'END';
574Klask - ports manager and finder for switch
575Copyright (C) 2005-2008 Gabriel Moreau
576
577END
578   print ' $Rev: 109 $'."\n";
579   print ' $Date: 2012-03-12 21:34:44 +0000 (Mon, 12 Mar 2012) $'."\n";
580   print ' $Id: klask 109 2012-03-12 21:34:44Z g7moreau $'."\n";
581   return;
582   }
583
584sub cmd_search {
585   my @computer = @_;
586
587   init_switch_names();    #nomme les switchs
588   fastping(@computer);
589   for my $clientname (@computer) {
590      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
591      my %where     = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement
592      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"
593         unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow';
594      }
595   return;
596   }
597
598sub cmd_searchdb {
599   my @computer = @_;
600
601   fastping(@computer);
602   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
603
604   LOOP_ON_COMPUTER:
605   for my $clientname (@computer) {
606      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
607      my $ip = $resol_arp{ipv4_address};
608
609      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
610
611      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
612      $year += 1900;
613      $mon++;
614      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
615
616      printf "%-22s %2s %-30s %-15s %-18s %s\n",
617         $computerdb->{$ip}{switch_name},
618         $computerdb->{$ip}{switch_port},
619         $computerdb->{$ip}{hostname_fq},
620         $ip,
621         $computerdb->{$ip}{mac_address},
622         $date;
623      }
624   return;
625   }
626
627sub cmd_updatedb {
628   my @network = @_;
629      @network = get_list_network() if not @network;
630
631   test_switchdb_environnement();
632
633   my $computerdb = {};
634      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
635   my $timestamp = time;
636
637   my %computer_not_detected = ();
638   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
639
640   my $number_of_computer = get_list_ip(@network); # + 1;
641   my $size_of_database   = keys %{$computerdb};
642      $size_of_database   = 1 if $size_of_database == 0;
643   my $i = 0;
644   my $detected_computer = 0;
645
646   init_switch_names('yes');    #nomme les switchs
647
648   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
649   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
650   my %db_switch_output_port       = %{$switch_connection->{output_port}};
651   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
652   my %db_switch_chained_port = ();
653   for my $swport (keys %db_switch_connected_on_port) {
654      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
655      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
656      }
657   for my $sw (@SWITCH){
658      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
659      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
660         chop $db_switch_chained_port{$sw->{hostname}};
661         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
662         }
663#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
664      }
665   }
666
667   my %router_mac_ip = ();
668   DETECT_ALL_ROUTER:
669#   for my $one_router ('194.254.66.254') {
670   for my $one_router ( get_list_main_router(@network) ) {
671      my %resol_arp = resolve_ip_arp_host($one_router);
672      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
673      }
674
675   ALL_NETWORK:
676   for my $net (@network) {
677
678      my @computer = get_list_ip($net);
679      my $current_interface = get_current_interface($net);
680
681      #fastping(@computer);
682
683      LOOP_ON_COMPUTER:
684      for my $one_computer (@computer) {
685         $i++;
686
687         my $total_percent = int (($i*100)/$number_of_computer);
688
689         my $localtime = time - $timestamp;
690         my ($sec,$min) = localtime $localtime;
691
692         my $time_elapse = 0;
693            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
694         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
695
696         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
697         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
698         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
699         printf ' %-8s %-14s', $current_interface, $one_computer;
700
701         my %resol_arp = resolve_ip_arp_host($one_computer, $current_interface);
702
703         # do not search on router connection (why ?)
704         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
705            $computer_not_detected{$one_computer} = $current_interface;
706            next LOOP_ON_COMPUTER;
707            }
708
709         # do not search on switch inter-connection
710         if (exists $switch_level{$resol_arp{hostname_fq}}) {
711            $computer_not_detected{$one_computer} = $current_interface;
712            next LOOP_ON_COMPUTER;
713            }
714
715         my $switch_proposal = q{};
716         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
717            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
718            }
719
720         # do not have a mac address
721         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
722            $computer_not_detected{$one_computer} = $current_interface;
723            next LOOP_ON_COMPUTER;
724            }
725
726         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal);
727
728         #192.168.24.156:
729         #  arp: 00:0B:DB:D5:F6:65
730         #  hostname: pcroyon.hmg.priv
731         #  port: 5
732         #  switch: sw-batH-legi:hp2524
733         #  timestamp: 1164355525
734
735         # do not have a mac address
736#         if ($resol_arp{mac_address} eq 'unknow') {
737#            $computer_not_detected{$one_computer} = $current_interface;
738#            next LOOP_ON_COMPUTER;
739#            }
740
741         # detected on a switch
742         if ($where{switch_description} ne 'unknow') {
743            $detected_computer++;
744            $computerdb->{$resol_arp{ipv4_address}} = {
745               hostname_fq        => $resol_arp{hostname_fq},
746               mac_address        => $resol_arp{mac_address},
747               switch_hostname    => $where{switch_hostname},
748               switch_description => $where{switch_description},
749               switch_port        => $where{switch_port},
750               timestamp          => $timestamp,
751               network            => $net,
752               };
753            next LOOP_ON_COMPUTER;
754            }
755
756         # new in the database but where it is ?
757         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
758            $detected_computer++;
759            $computerdb->{$resol_arp{ipv4_address}} = {
760               hostname_fq        => $resol_arp{hostname_fq},
761               mac_address        => $resol_arp{mac_address},
762               switch_hostname    => $where{switch_hostname},
763               switch_description => $where{switch_description},
764               switch_port        => $where{switch_port},
765               timestamp          => $resol_arp{timestamp},
766               network            => $net,
767               };
768            }
769
770         # mise a jour du nom de la machine si modification dans le dns
771         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
772
773         # mise à jour de la date de détection si détection plus récente par arpwatch
774         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
775
776         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
777#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
778         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
779
780         }
781      }
782
783   # final end of line at the end of the loop
784   printf "\n";
785
786   my $dirdb = $KLASK_DB_FILE;
787      $dirdb =~ s{ / [^/]* $}{}xms;
788   mkdir "$dirdb", 0755 unless -d "$dirdb";
789   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
790
791   for my $one_computer (keys %computer_not_detected) {
792      my $interface = $computer_not_detected{$one_computer};
793      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
794#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
795      }
796   return;
797   }
798
799sub cmd_removedb {
800   my @computer = @_;
801
802   test_maindb_environnement();
803
804   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
805
806   LOOP_ON_COMPUTER:
807   for my $one_computer (@computer) {
808
809      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
810            and exists $computerdb->{$one_computer} ) {
811         delete $computerdb->{$one_computer};
812         next;
813         }
814
815      my %resol_arp = resolve_ip_arp_host($one_computer);
816
817      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
818      }
819
820   my $dirdb = $KLASK_DB_FILE;
821      $dirdb =~ s{ / [^/]* $}{}xms;
822   mkdir "$dirdb", 0755 unless -d "$dirdb";
823   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
824   return;
825   }
826
827sub cmd_cleandb {
828   my @options = @_;
829
830   my $days_to_clean = 15;
831   my $verbose;
832   my $database_has_changed;
833
834   my $ret = GetOptionsFromArray(\@options,
835      'day|d=i'   => \$days_to_clean,
836      'verbose|v' => \$verbose,
837      );
838
839   my @vlan_name = get_list_network();
840
841   my $computerdb = {};
842      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
843   my $timestamp = time;
844
845   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
846   my $timestamp_3month  = 3600 * 24 * 90;
847
848   my %mactimedb = ();
849   ALL_VLAN:
850   for my $vlan (shuffle @vlan_name) {
851
852      my @ip_list   = shuffle get_list_ip($vlan);
853     
854      LOOP_ON_IP_ADDRESS:
855      for my $ip (@ip_list) {
856
857         next LOOP_ON_IP_ADDRESS if
858            not exists $computerdb->{$ip};
859           
860            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
861         my $ip_timestamp   = $computerdb->{$ip}{timestamp};
862         my $ip_mac         = $computerdb->{$ip}{mac_address};
863         my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
864
865         $mactimedb{$ip_mac} ||= {
866            ip          => $ip,
867            timestamp   => $ip_timestamp,
868            vlan        => $vlan,
869            hostname_fq => $ip_hostname_fq,
870            };
871         
872         if (
873            ( $mactimedb{$ip_mac}->{timestamp} - $ip_timestamp > $timestamp_barrier
874               or (
875                  $mactimedb{$ip_mac}->{timestamp} > $ip_timestamp
876                  and $timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_3month
877                  )
878            )
879            and (
880               not $mactimedb{$ip_mac}->{hostname_fq} =~ m/^float/
881               or $ip_hostname_fq =~ m/^float/
882               )) {
883            print "remove ip $ip\n" if $verbose;
884            delete $computerdb->{$ip};
885            $database_has_changed++;
886            }
887
888         elsif (
889            ( $ip_timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_barrier
890               or (
891                  $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}
892                  and $timestamp - $ip_timestamp > $timestamp_3month
893                  )
894            )
895            and (
896               not $ip_hostname_fq =~ m/^float/
897               or $mactimedb{$ip_mac}->{hostname_fq} =~ m/^float/
898               )) {
899            print "remove ip ".$mactimedb{$ip_mac}->{ip}."\n" if $verbose;
900            delete $computerdb->{$mactimedb{$ip_mac}->{ip}};
901            $database_has_changed++;
902            }
903
904         if ( $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}) {
905            $mactimedb{$ip_mac} = {
906               ip          => $ip,
907               timestamp   => $ip_timestamp,
908               vlan        => $vlan,
909               hostname_fq => $ip_hostname_fq,
910               };
911            }
912         }
913      }
914
915   if ( $database_has_changed ) {
916      my $dirdb = $KLASK_DB_FILE;
917         $dirdb =~ s{ / [^/]* $}{}xms;
918      mkdir "$dirdb", 0755 unless -d "$dirdb";
919      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
920      }
921   return;
922   }
923
924sub cmd_exportdb {
925   my @ARGV   = @_;
926
927   my $format = 'txt';
928
929   my $ret = GetOptions(
930      'format|f=s'  => \$format,
931      );
932
933   my %possible_format = (
934      txt  => \&cmd_exportdb_txt,
935      html => \&cmd_exportdb_html,
936      );
937
938   $format = 'txt' if not defined $possible_format{$format};
939
940   $possible_format{$format}->(@ARGV);
941   return;
942   }
943
944sub cmd_exportdb_txt {
945   test_maindb_environnement();
946
947   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
948
949   printf "%-27s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
950   print "--------------------------------------------------------------------------------------------------------------------------------------------\n";
951
952   LOOP_ON_IP_ADDRESS:
953   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
954
955#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
956
957      # to be improve in the future
958      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
959
960# dans le futur
961#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
962
963      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
964      $year += 1900;
965      $mon++;
966      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
967
968      printf "%-28s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
969         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
970         $computerdb->{$ip}{switch_port},
971         $computerdb->{$ip}{hostname_fq},
972         $ip,
973         $computerdb->{$ip}{mac_address},
974         $date,
975         $computerdb->{$ip}{network} || '';
976      }
977   return;
978   }
979
980sub cmd_exportdb_html {
981   test_maindb_environnement();
982
983   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
984
985#<link rel="stylesheet" type="text/css" href="style-klask.css" />
986#<script src="sorttable-klask.js"></script>
987
988   print <<'END_HTML';
989<table class="sortable" summary="Klask Host Database">
990 <caption>Klask Host Database</caption>
991 <thead>
992  <tr>
993   <th scope="col" class="klask-header-left">Switch</th>
994   <th scope="col" class="sorttable_nosort">Port</th>
995   <th scope="col" class="sorttable_nosort">Link</th>
996   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
997   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
998   <th scope="col" class="sorttable_alpha">MAC-Address</th>
999   <th scope="col" class="sorttable_alpha">VLAN</th>
1000   <th scope="col" class="klask-header-right">Date</th>
1001  </tr>
1002 </thead>
1003 <tfoot>
1004  <tr>
1005   <th scope="col" class="klask-footer-left">Switch</th>
1006   <th scope="col" class="fklask-port">Port</th>
1007   <th scope="col" class="fklask-link">Link</th>
1008   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1009   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
1010   <th scope="col" class="fklask-mac">MAC-Address</th>
1011   <th scope="col" class="fklask-vlan">VLAN</th>
1012   <th scope="col" class="klask-footer-right">Date</th>
1013  </tr>
1014 </tfoot>
1015 <tbody>
1016END_HTML
1017
1018   my %mac_count = ();
1019   LOOP_ON_IP_ADDRESS:
1020   foreach my $ip (keys %{$computerdb}) {
1021
1022      # to be improve in the future
1023      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1024
1025      $mac_count{$computerdb->{$ip}{mac_address}}++;
1026      }
1027
1028   my $typerow = 'even';
1029
1030   LOOP_ON_IP_ADDRESS:
1031   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1032
1033      # to be improve in the future
1034      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1035
1036      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1037      $year += 1900;
1038      $mon++;
1039      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1040
1041#      $odd_or_even++;
1042#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
1043      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1044
1045      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1046      chomp $switch_hostname;
1047      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
1048
1049      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1050
1051      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
1052
1053      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1054      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
1055
1056      my $vlan = $computerdb->{$ip}{network} || '';
1057
1058      print <<"END_HTML";
1059  <tr class="$typerow">
1060   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
1061   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
1062   <td><-------</td>
1063   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1064   <td sorttable_customkey="$ip_sort">$ip</td>
1065   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
1066   <td>$vlan</td>
1067   <td>$date</td>
1068  </tr>
1069END_HTML
1070      }
1071
1072   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1073
1074   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1075   my %db_switch_parent            = %{$switch_connection->{parent}};
1076   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1077   my %db_switch                   = %{$switch_connection->{switch_db}};
1078
1079   for my $sw (sort keys %db_switch_output_port) {
1080
1081      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
1082
1083      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1084
1085      if (exists $db_switch_parent{$sw}) {
1086
1087      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1088      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1089      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1090
1091      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1092      $year += 1900;
1093      $mon++;
1094      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1095
1096      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1097
1098      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1099
1100      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
1101
1102      print <<"END_HTML";
1103  <tr class="$typerow">
1104   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1105   <td class="bklask-port">$db_switch_output_port{$sw}</>
1106   <td>+--> $db_switch_parent{$sw}->{port}</td>
1107   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1108   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1109   <td sorttable_customkey="$mac_sort">$mac_address</td>
1110   <td></td>
1111   <td>$date</td>
1112  </tr>
1113END_HTML
1114         }
1115      else {
1116         print <<"END_HTML";
1117  <tr class="$typerow">
1118   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1119   <td class="bklask-port">$db_switch_output_port{$sw}</>
1120   <td>+--></td>
1121   <td sorttable_customkey="router">router</>
1122   <td sorttable_customkey="999999999999"></td>
1123   <td sorttable_customkey="99999"></td>
1124   <td></td>
1125   <td></td>
1126  </tr>
1127END_HTML
1128         }
1129      }
1130
1131   for my $swport (sort keys %db_switch_connected_on_port) {
1132      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1133      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1134
1135         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
1136
1137      my $mac_address = $db_switch{$sw}->{mac_address};
1138      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1139      my $timestamp = $db_switch{$sw}->{timestamp};
1140
1141      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1142      $year += 1900;
1143      $mon++;
1144      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
1145
1146      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1147
1148      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1149
1150      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1151
1152         if (exists $db_switch_output_port{$sw}) {
1153
1154            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
1155
1156            print <<"END_HTML";
1157  <tr class="$typerow">
1158   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1159   <td class="bklask-port">$port_connect</>
1160   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1161   <td sorttable_customkey="$host_short">$sw</>
1162   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1163   <td sorttable_customkey="$mac_sort">$mac_address</td>
1164   <td></td>
1165   <td>$date</td>
1166  </tr>
1167END_HTML
1168            }
1169         else {
1170            print <<"END_HTML";
1171  <tr class="$typerow">
1172   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1173   <td class="bklask-port">$port_connect</>
1174   <td>&lt;--+</td>
1175   <td sorttable_customkey="$sw">$sw</>
1176   <td sorttable_customkey="">$ipv4_address</td>
1177   <td sorttable_customkey="">$mac_address</td>
1178   <td></td>
1179   <td>$date</td>
1180  </tr>
1181END_HTML
1182            }
1183         }
1184      }
1185
1186   print <<'END_HTML';
1187 </tbody>
1188</table>
1189END_HTML
1190   return;
1191   }
1192
1193sub cmd_iplocation {
1194   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1195
1196   LOOP_ON_IP_ADDRESS:
1197   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1198
1199      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1200
1201      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
1202      next if $sw_hostname eq 'unknow';
1203
1204      my $sw_location = q{};
1205      for my $sw (@SWITCH) {
1206         next if $sw_hostname ne $sw->{hostname};
1207         $sw_location = $sw->{location};
1208         last;
1209         }
1210
1211      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
1212      }
1213   return;
1214   }
1215
1216sub cmd_ip_free {
1217   my @vlan_name = @_;
1218
1219   my $days_to_dead = 365 * 2;
1220   my $format = 'txt';
1221   my $verbose;
1222
1223   my $ret = GetOptionsFromArray(\@vlan_name,
1224      'day|d=i'      => \$days_to_dead,
1225      'format|f=s'   => \$format,
1226      'verbose|v'    => \$verbose,
1227      );
1228
1229   my %possible_format = (
1230      txt  => \&cmd_ip_free_txt,
1231      html => \&cmd_ip_free_html,
1232      none => sub {},
1233      );
1234   $format = 'txt' if not defined $possible_format{$format};
1235
1236   @vlan_name = get_list_network() if not @vlan_name;
1237
1238   my $computerdb = {};
1239      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1240   my $timestamp = time;
1241
1242   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1243
1244   my %result_ip = ();
1245
1246   ALL_NETWORK:
1247   for my $vlan (@vlan_name) {
1248
1249      my @ip_list = get_list_ip($vlan);
1250
1251      LOOP_ON_IP_ADDRESS:
1252      for my $ip (@ip_list) {
1253
1254         next LOOP_ON_IP_ADDRESS if
1255            exists $computerdb->{$ip}
1256            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1257
1258         my $ip_date_last_detection = '';
1259         if (exists $computerdb->{$ip}) {
1260            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1261            $year += 1900;
1262            $mon++;
1263            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1264            }
1265
1266         $result_ip{$ip} ||= {};
1267         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1268
1269         my $packed_ip = scalar gethostbyname($ip);
1270         my $hostname_fq = 'unknown';
1271            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1272         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1273
1274         $result_ip{$ip}->{vlan} = $vlan;
1275
1276         printf "VERBOSE_1: %-15s %-12s %s\n", $ip, $vlan, $hostname_fq if $verbose;
1277         }
1278      }
1279
1280   $possible_format{$format}->(%result_ip);
1281   }
1282
1283sub cmd_ip_free_txt {
1284   my %result_ip = @_;
1285   
1286   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1287   print "-------------------------------------------------------------------------------\n";
1288   LOOP_ON_IP_ADDRESS:
1289   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1290         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1291      }
1292   }
1293
1294sub cmd_ip_free_html {
1295   my %result_ip = @_;
1296
1297   print <<'END_HTML';
1298<table class="sortable" summary="Klask Free IP Database">
1299 <caption>Klask Free IP Database</caption>
1300 <thead>
1301  <tr>
1302   <th scope="col" class="klask-header-left">IPv4-Address</th>
1303   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1304   <th scope="col" class="sorttable_alpha">VLAN</th>
1305   <th scope="col" class="klask-header-right">Date</th>
1306  </tr>
1307 </thead>
1308 <tfoot>
1309  <tr>
1310   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1311   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1312   <th scope="col" class="fklask-vlan">VLAN</th>
1313   <th scope="col" class="klask-footer-right">Date</th>
1314  </tr>
1315 </tfoot>
1316 <tbody>
1317END_HTML
1318
1319   my $typerow = 'even';
1320
1321   LOOP_ON_IP_ADDRESS:
1322   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1323
1324      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1325
1326      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1327      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1328
1329      print <<"END_HTML";
1330  <tr class="$typerow">
1331   <td sorttable_customkey="$ip_sort">$ip</td>
1332   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
1333   <td>$result_ip{$ip}->{vlan}</td>
1334   <td>$result_ip{$ip}->{date_last_detection}</td>
1335  </tr>
1336END_HTML
1337      }
1338   print <<'END_HTML';
1339 </tbody>
1340</table>
1341END_HTML
1342   }
1343
1344sub cmd_enable {
1345   my $switch = shift;
1346   my $port   = shift;
1347
1348   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1349   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
1350   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
1351   return;
1352   }
1353
1354sub cmd_disable {
1355   my $switch = shift;
1356   my $port   = shift;
1357
1358   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
1359   return;
1360   }
1361
1362sub cmd_status {
1363   my $switch = shift;
1364   my $port   = shift;
1365
1366   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
1367   return;
1368   }
1369
1370sub cmd_search_mac_on_switch {
1371   my $switch_name = shift || q{};
1372   my $mac_address = shift || q{};
1373
1374   if ($switch_name eq q{} or $mac_address eq q{}) {
1375      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1376      }
1377
1378   if (not defined $SWITCH_DB{$switch_name}) {
1379      die "Switch $switch_name must be defined in klask configuration file\n";
1380      }
1381
1382   my $sw = $SWITCH_DB{$switch_name};
1383   my %session = ( -hostname => $sw->{hostname} );
1384      $session{-version} = $sw->{version}   || 1;
1385      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1386   if (exists $sw->{version} and $sw->{version} eq '3') {
1387      $session{-username} = $sw->{username} || 'snmpadmin';
1388      }
1389   else {
1390      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1391      }
1392
1393   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
1394   print "Klask search OID $research on switch $switch_name\n";
1395
1396   my ($session, $error) = Net::SNMP->session( %session );
1397   print "$error \n" if $error;
1398
1399   my $result = $session->get_request(
1400      -varbindlist => [$research]
1401      );
1402
1403   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
1404      print "Klask do not find MAC $mac_address on switch $switch_name\n";
1405      $session->close;
1406      }
1407
1408   my $swport = $result->{$research};
1409   $session->close;
1410
1411   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
1412   return;
1413   }
1414
1415sub cmd_updatesw {
1416   my @options = @_;
1417
1418   my $verbose;
1419
1420   my $ret = GetOptionsFromArray(\@options,
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.inpg.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 109 2012-03-12 21:34:44Z 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-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.